wikiparser-node 1.9.1 → 1.9.2

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.
package/README.md CHANGED
@@ -4,54 +4,54 @@
4
4
 
5
5
  # Other Languages
6
6
 
7
- - [English](./README.en.md)
7
+ - [简体中文](./README-%28ZH%29.md)
8
8
 
9
- # 简介
9
+ # Introduction
10
10
 
11
- WikiParser-Node 是一款由 Bhsd 开发的基于 [Node.js](https://nodejs.org/) 环境的离线[维基文本](https://www.mediawiki.org/wiki/Wikitext)语法解析器,可以解析几乎全部的维基语法并生成[语法树](https://en.wikipedia.org/wiki/Abstract_syntax_tree)[在线解析](https://bhsd-harry.github.io/wikiparser-node/#editor)),还可以很方便地对语法树进行查询和修改,最后返回修改后的维基文本。
11
+ WikiParser-Node is an offline [Wikitext](https://www.mediawiki.org/wiki/Wikitext) parser developed by Bhsd for the [Node.js](https://nodejs.org/) environment. It can parse almost all wiki syntax and generate an [Abstract Syntax Tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree) ([Try it online](https://bhsd-harry.github.io/wikiparser-node/#editor)). It also allows for easy querying and modification of the AST, and returns the modified wikitext.
12
12
 
13
- # 其他版本
13
+ # Other Versions
14
14
 
15
- ## Mini (又名 [WikiLint](https://www.npmjs.com/package/wikilint))
15
+ ## Mini (also known as [WikiLint](https://www.npmjs.com/package/wikilint))
16
16
 
17
- 提供了 [CLI](https://en.wikipedia.org/wiki/Command-line_interface),但仅保留了解析功能和语法错误分析功能,解析生成的语法树不能修改。这个版本被应用于 [eslint-plugin-wikitext](https://www.npmjs.com/package/eslint-plugin-wikitext) 插件。
17
+ This version provides a [CLI](https://en.wikipedia.org/wiki/Command-line_interface), but only retains the parsing functionality and linting functionality. The parsed AST cannot be modified. It is used in the [eslint-plugin-wikitext](https://www.npmjs.com/package/eslint-plugin-wikitext) plugin.
18
18
 
19
19
  ## Browser-compatible
20
20
 
21
- 兼容浏览器的版本,可用于代码高亮或是搭配 [CodeMirror](https://codemirror.net/) [Monaco](https://microsoft.github.io/monaco-editor/) 等编辑器作为语法分析插件。([使用实例展示](https://bhsd-harry.github.io/wikiparser-node)
21
+ A browser-compatible version, which can be used for code highlighting or as a linting plugin in conjunction with editors such as [CodeMirror](https://codemirror.net/) and [Monaco](https://microsoft.github.io/monaco-editor/). ([Usage example](https://bhsd-harry.github.io/wikiparser-node))
22
22
 
23
- # 安装方法
23
+ # Installation
24
24
 
25
25
  ## Node.js
26
26
 
27
- 请根据需要需要安装对应的版本(`WikiParser-Node` `WikiLint`),如:
27
+ Please install the corresponding version as needed (`WikiParser-Node` or `WikiLint`), for example:
28
28
 
29
29
  ```sh
30
30
  npm i wikiparser-node
31
31
  ```
32
32
 
33
-
33
+ or
34
34
 
35
35
  ```sh
36
36
  npm i wikilint
37
37
  ```
38
38
 
39
- ## 浏览器
39
+ ## Browser
40
40
 
41
- 可以通过 CDN 下载代码,如:
41
+ You can download the code via CDN, for example:
42
42
 
43
43
  ```html
44
- <script src="//cdn.jsdelivr.net/npm/wikiparser-node@browser"></script>
44
+ <script src="//cdn.jsdelivr.net/npm/wikiparser-node@browser/bundle/bundle.min.js"></script>
45
45
  ```
46
46
 
47
-
47
+ or
48
48
 
49
49
  ```html
50
- <script src="//unpkg.com/wikiparser-node@browser"></script>
50
+ <script src="//unpkg.com/wikiparser-node@browser/bundle/bundle.min.js"></script>
51
51
  ```
52
52
 
53
- 更多浏览器端可用的插件请查阅对应[文档](https://github.com/bhsd-harry/wikiparser-node/wiki/Browser)
53
+ For more browser extensions, please refer to the corresponding [documentation](https://github.com/bhsd-harry/wikiparser-node/wiki/Browser-%28EN%29).
54
54
 
55
- # 使用方法
55
+ # Usage
56
56
 
57
- 请查阅 [Wiki](https://github.com/bhsd-harry/wikiparser-node/wiki)
57
+ Please refer to the [Wiki](https://github.com/bhsd-harry/wikiparser-node/wiki/Home-%28EN%29).
@@ -33,6 +33,7 @@ index_1.Token.prototype.createElement =
33
33
  return debug_1.Shadow.run(() => {
34
34
  // @ts-expect-error abstract class
35
35
  const attr = new attributes_1.AttributesToken(undefined, 'html-attrs', tagName, config);
36
+ attr.afterBuild();
36
37
  // @ts-expect-error abstract class
37
38
  return new html_1.HtmlToken(tagName, attr, Boolean(closing), Boolean(selfClosing), config);
38
39
  });
package/dist/index.js CHANGED
@@ -14,14 +14,26 @@ const diff_1 = require("./util/diff");
14
14
  * @param dir 子路径
15
15
  */
16
16
  const rootRequire = (file, dir) => require(file.startsWith('/') ? file : `../${file.includes('/') ? '' : dir}${file}`);
17
+ /* NOT FOR BROWSER */
17
18
  const promises = [Promise.resolve()];
19
+ let viewOnly = false;
20
+ /* NOT FOR BROWSER END */
18
21
  // eslint-disable-next-line @typescript-eslint/no-redeclare
19
22
  const Parser = {
20
23
  config: 'default',
21
24
  i18n: undefined,
22
25
  rules: base_1.rules,
23
26
  /* NOT FOR BROWSER */
24
- viewOnly: false,
27
+ /** @implements */
28
+ get viewOnly() {
29
+ return viewOnly;
30
+ },
31
+ set viewOnly(value) {
32
+ if (viewOnly && !value) {
33
+ debug_1.Shadow.rev++;
34
+ }
35
+ viewOnly = value;
36
+ },
25
37
  conversionTable: new Map(),
26
38
  redirects: new Map(),
27
39
  warning: true,
@@ -62,24 +74,23 @@ const Parser = {
62
74
  return new Title(title, defaultNs, config, decode, selfLink);
63
75
  }
64
76
  const { Token } = require('./src/index');
65
- const token = debug_1.Shadow.run(() => {
77
+ return debug_1.Shadow.run(() => {
66
78
  const root = new Token(title, config);
67
79
  root.type = 'root';
68
- return root.parseOnce(0, include).parseOnce();
69
- }), titleObj = new Title(token.toString(), defaultNs, config, decode, selfLink);
70
- debug_1.Shadow.run(() => {
80
+ root.parseOnce(0, include).parseOnce();
81
+ const titleObj = new Title(root.toString(), defaultNs, config, decode, selfLink);
71
82
  for (const key of ['main', 'fragment']) {
72
83
  const str = titleObj[key];
73
84
  if (str?.includes('\0')) {
74
- titleObj[key] = token.buildFromStr(str, constants_1.BuildMethod.Text);
85
+ titleObj[key] = root.buildFromStr(str, constants_1.BuildMethod.Text);
75
86
  }
76
87
  }
88
+ /* NOT FOR BROWSER */
89
+ titleObj.conversionTable = this.conversionTable;
90
+ titleObj.redirects = this.redirects;
91
+ /* NOT FOR BROWSER END */
92
+ return titleObj;
77
93
  });
78
- /* NOT FOR BROWSER */
79
- titleObj.conversionTable = this.conversionTable;
80
- titleObj.redirects = this.redirects;
81
- /* NOT FOR BROWSER END */
82
- return titleObj;
83
94
  },
84
95
  /** @implements */
85
96
  parse(wikitext, include, maxStage = constants_1.MAX_STAGE, config = Parser.getConfig()) {
@@ -148,6 +159,7 @@ const Parser = {
148
159
  ...Object.entries(constants_1.classes),
149
160
  ...Object.entries(constants_1.mixins),
150
161
  ...Object.entries(constants_1.parsers),
162
+ ...Object.entries(constants_1.constants),
151
163
  ];
152
164
  for (const [, filePath] of entries) {
153
165
  try {
@@ -182,7 +194,7 @@ const Parser = {
182
194
  }
183
195
  const file = path.join(__dirname, '..', 'errors', main), wikitext = fs.readFileSync(file, 'utf8');
184
196
  const { stage, include, config } = require(`${file}.json`), { Token } = require('./src/index');
185
- return debug_1.Shadow.run(() => {
197
+ debug_1.Shadow.run(() => {
186
198
  const halfParsed = stage < constants_1.MAX_STAGE, token = new Token(halfParsed ? wikitext : (0, string_1.tidy)(wikitext), config);
187
199
  token.type = 'root';
188
200
  if (halfParsed) {
@@ -195,7 +207,6 @@ const Parser = {
195
207
  fs.unlinkSync(file);
196
208
  fs.unlinkSync(`${file}.err`);
197
209
  fs.unlinkSync(`${file}.json`);
198
- return token;
199
210
  });
200
211
  },
201
212
  };
package/dist/lib/node.js CHANGED
@@ -163,15 +163,7 @@ class AstNode {
163
163
  /* NOT FOR BROWSER END */
164
164
  /** @private */
165
165
  getAttribute(key) {
166
- if (key === 'padding') {
167
- return 0;
168
- /* NOT FOR BROWSER */
169
- }
170
- else if (key === 'optional') {
171
- return this.#optional;
172
- /* NOT FOR BROWSER END */
173
- }
174
- return this[key];
166
+ return (key === 'padding' ? 0 : this[key]);
175
167
  }
176
168
  /** @private */
177
169
  setAttribute(key, value) {
@@ -22,7 +22,7 @@ const closes = {
22
22
  * @throws TranscludeToken.constructor()
23
23
  */
24
24
  const parseBraces = (wikitext, config, accum) => {
25
- const source = String.raw `${config.excludes?.includes('heading') ? '' : String.raw `^(\0\d+c\x7F)*={1,6}|`}\[\[|\{{2,}|-\{(?!\{)`, { parserFunction: [, , , subst] } = config, stack = [];
25
+ const source = String.raw `${config.excludes?.includes('heading') ? '' : String.raw `^(\0\d+c\x7F)*={1,6}|`}\[\[|-\{(?!\{)`, openBraces = String.raw `|\{{2,}`, { parserFunction: [, , , subst] } = config, stack = [];
26
26
  wikitext = wikitext.replace(re, (m, p1) => {
27
27
  // @ts-expect-error abstract class
28
28
  new transclude_1.TranscludeToken(m.slice(2, -2), [], config, accum);
@@ -30,8 +30,8 @@ const parseBraces = (wikitext, config, accum) => {
30
30
  });
31
31
  const lastBraces = wikitext.lastIndexOf('}}') - wikitext.length;
32
32
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
33
- /^(\0\d+c\x7F)*={1,6}|\[\[|\{{2,}|-\{(?!\{)|[\n|=]|\}{2,}|\}-|\]\]/gmu;
34
- let regex = new RegExp(source, 'gmu'), mt = regex.exec(wikitext), moreBraces = lastBraces + wikitext.length !== -1, lastIndex;
33
+ /^(\0\d+c\x7F)*={1,6}|\[\[|-\{(?!\{)|\{{2,}|[\n|=]|\}{2,}|\}-|\]\]/gmu;
34
+ let moreBraces = lastBraces + wikitext.length !== -1, regex = new RegExp(source + (moreBraces ? openBraces : ''), 'gmu'), mt = regex.exec(wikitext), lastIndex;
35
35
  while (mt
36
36
  || lastIndex !== undefined && lastIndex <= wikitext.length && stack[stack.length - 1]?.[0]?.startsWith('=')) {
37
37
  if (mt?.[1]) {
@@ -130,13 +130,17 @@ const parseBraces = (wikitext, config, accum) => {
130
130
  }
131
131
  stack.push(...'0' in top ? [top] : [], mt);
132
132
  }
133
- moreBraces &&= lastBraces + wikitext.length >= lastIndex;
134
133
  let curTop = stack[stack.length - 1];
135
- if (!moreBraces && curTop?.[0]?.startsWith('{')) {
136
- stack.pop();
137
- curTop = stack[stack.length - 1];
134
+ if (moreBraces && lastBraces + wikitext.length < lastIndex) {
135
+ moreBraces = false;
136
+ while (curTop?.[0]?.startsWith('{')) {
137
+ stack.pop();
138
+ curTop = stack[stack.length - 1];
139
+ }
138
140
  }
139
- regex = new RegExp(source + (curTop ? `|${closes[curTop[0][0]]}${curTop.findEqual ? '|=' : ''}` : ''), 'gmu');
141
+ regex = new RegExp(source
142
+ + (moreBraces ? openBraces : '')
143
+ + (curTop ? `|${closes[curTop[0][0]]}${curTop.findEqual ? '|=' : ''}` : ''), 'gmu');
140
144
  regex.lastIndex = lastIndex;
141
145
  mt = regex.exec(wikitext);
142
146
  }
package/dist/src/arg.js CHANGED
@@ -127,7 +127,6 @@ class ArgToken extends index_2.Token {
127
127
  const token = new ArgToken([''], this.getAttribute('config'));
128
128
  token.firstChild.safeReplaceWith(name);
129
129
  token.append(...cloned);
130
- token.afterBuild();
131
130
  return token;
132
131
  });
133
132
  }
@@ -138,6 +137,7 @@ class ArgToken extends index_2.Token {
138
137
  /** @private */
139
138
  afterBuild() {
140
139
  this.#setName();
140
+ super.afterBuild();
141
141
  const /** @implements */ argListener = ({ prevTarget }) => {
142
142
  if (prevTarget === this.firstChild) {
143
143
  this.#setName();
@@ -321,6 +321,7 @@ let AttributeToken = (() => {
321
321
  this.#tag = this.parentNode.name;
322
322
  }
323
323
  this.setAttribute('name', this.firstChild.text().trim().toLowerCase());
324
+ super.afterBuild();
324
325
  }
325
326
  /** @private */
326
327
  toString() {
@@ -408,13 +409,6 @@ let AttributeToken = (() => {
408
409
  return this.#equal ? super.print({ sep: (0, string_1.escape)(this.#equal) + quoteStart, post: quoteEnd }) : super.print();
409
410
  }
410
411
  /* NOT FOR BROWSER */
411
- /** @private */
412
- getAttribute(key) {
413
- if (key === 'equal') {
414
- return this.#equal;
415
- }
416
- return key === 'quotes' ? this.#quotes : super.getAttribute(key);
417
- }
418
412
  /** @override */
419
413
  cloneNode() {
420
414
  const [key, value] = this.cloneChildNodes(), config = this.getAttribute('config');
@@ -423,7 +417,6 @@ let AttributeToken = (() => {
423
417
  const token = new AttributeToken(this.type, this.tag, '', this.#equal, '', this.#quotes, config);
424
418
  token.firstChild.safeReplaceWith(key);
425
419
  token.lastChild.safeReplaceWith(value);
426
- token.setAttribute('name', this.name);
427
420
  return token;
428
421
  });
429
422
  }
@@ -114,10 +114,11 @@ class AttributesToken extends index_2.Token {
114
114
  }
115
115
  /** @private */
116
116
  afterBuild() {
117
- if (this.type === 'table-attrs') {
118
- const { parentNode } = this;
119
- this.setAttribute('name', parentNode?.type === 'td' && parentNode.subtype === 'caption' ? 'caption' : parentNode?.type);
117
+ const { parentNode } = this;
118
+ if (parentNode?.type === 'td' && parentNode.subtype === 'caption') {
119
+ this.setAttribute('name', 'caption');
120
120
  }
121
+ super.afterBuild();
121
122
  }
122
123
  /**
123
124
  * 所有指定属性名的AttributeToken
@@ -208,7 +209,6 @@ class AttributesToken extends index_2.Token {
208
209
  // @ts-expect-error abstract class
209
210
  const token = new AttributesToken(undefined, this.type, this.name, this.getAttribute('config'));
210
211
  token.append(...cloned);
211
- token.setAttribute('name', this.name);
212
212
  return token;
213
213
  });
214
214
  }
@@ -37,6 +37,7 @@ class ConverterFlagsToken extends index_2.Token {
37
37
  /** @private */
38
38
  afterBuild() {
39
39
  this.#flags = this.childNodes.map(child => child.text().trim());
40
+ super.afterBuild();
40
41
  /* NOT FOR BROWSER */
41
42
  const /** @implements */ converterFlagsListener = ({ prevTarget }) => {
42
43
  if (prevTarget) {
@@ -112,14 +113,9 @@ class ConverterFlagsToken extends index_2.Token {
112
113
  // @ts-expect-error abstract class
113
114
  const token = new ConverterFlagsToken([], this.getAttribute('config'));
114
115
  token.append(...cloned);
115
- token.afterBuild();
116
116
  return token;
117
117
  });
118
118
  }
119
- /** @private */
120
- getAttribute(key) {
121
- return key === 'flags' ? this.#flags : super.getAttribute(key);
122
- }
123
119
  /**
124
120
  * @override
125
121
  * @param i 移除位置
@@ -116,12 +116,12 @@ class ConverterRuleToken extends index_2.Token {
116
116
  for (let i = 0; i < cloned.length; i++) {
117
117
  token.childNodes[i].safeReplaceWith(cloned[i]);
118
118
  }
119
- token.afterBuild();
120
119
  return token;
121
120
  });
122
121
  }
123
122
  /** @private */
124
123
  afterBuild() {
124
+ super.afterBuild();
125
125
  const /** @implements */ converterRuleListener = (e, data) => {
126
126
  const { prevTarget } = e;
127
127
  if (this.length > 1 && this.childNodes[this.length - 2] === prevTarget) {
package/dist/src/html.js CHANGED
@@ -168,11 +168,6 @@ let HtmlToken = (() => {
168
168
  }
169
169
  /** @private */
170
170
  getAttribute(key) {
171
- /* NOT FOR BROWSER */
172
- if (key === 'tag') {
173
- return this.#tag;
174
- }
175
- /* NOT FOR BROWSER END */
176
171
  return key === 'padding'
177
172
  ? this.#tag.length + (this.closing ? 2 : 1)
178
173
  : super.getAttribute(key);
@@ -151,6 +151,7 @@ class ImageParameterToken extends index_2.Token {
151
151
  if (this.parentNode?.type === 'gallery-image' && !exports.galleryParams.has(this.name)) {
152
152
  this.setAttribute('name', 'invalid');
153
153
  }
154
+ super.afterBuild();
154
155
  }
155
156
  /** @private */
156
157
  toString() {
@@ -164,11 +165,6 @@ class ImageParameterToken extends index_2.Token {
164
165
  getAttribute(key) {
165
166
  if (key === 'plain') {
166
167
  return (this.name === 'caption');
167
- /* NOT FOR BROWSER */
168
- }
169
- else if (key === 'syntax') {
170
- return this.#syntax;
171
- /* NOT FOR BROWSER END */
172
168
  }
173
169
  return key === 'padding'
174
170
  ? Math.max(0, this.#syntax.indexOf('$1'))
@@ -213,20 +209,9 @@ class ImageParameterToken extends index_2.Token {
213
209
  // @ts-expect-error abstract class
214
210
  const token = new ImageParameterToken(this.#syntax.replace('$1', ''), this.#extension, config);
215
211
  token.replaceChildren(...cloned);
216
- token.setAttribute('name', this.name);
217
- token.setAttribute('syntax', this.#syntax);
218
212
  return token;
219
213
  });
220
214
  }
221
- /** @private */
222
- setAttribute(key, value) {
223
- if (key === 'syntax') {
224
- this.#syntax = value;
225
- }
226
- else {
227
- super.setAttribute(key, value);
228
- }
229
- }
230
215
  insertAt(token, i) {
231
216
  if (!debug_1.Shadow.running && this.#isVoid()) {
232
217
  throw new Error(`Image parameter ${this.name} does not accept custom input!`);
package/dist/src/index.js CHANGED
@@ -197,8 +197,8 @@ class Token extends element_1.AstElement {
197
197
  }
198
198
  return nodes;
199
199
  }
200
- /** 将占位符替换为子Token */
201
- #build() {
200
+ /** @private */
201
+ build() {
202
202
  this.#stage = constants_1.MAX_STAGE;
203
203
  const { length, firstChild } = this, str = String(firstChild);
204
204
  if (length === 1 && firstChild.type === 'text' && str.includes('\0')) {
@@ -206,7 +206,7 @@ class Token extends element_1.AstElement {
206
206
  this.normalize();
207
207
  if (this.type === 'root') {
208
208
  for (const token of this.#accum) {
209
- token.#build();
209
+ token.build();
210
210
  }
211
211
  }
212
212
  }
@@ -227,7 +227,7 @@ class Token extends element_1.AstElement {
227
227
  this.parseOnce(this.#stage, include);
228
228
  }
229
229
  if (n) {
230
- this.#build();
230
+ this.build();
231
231
  this.afterBuild();
232
232
  }
233
233
  return this;
@@ -343,6 +343,8 @@ class Token extends element_1.AstElement {
343
343
  return (this.#include ?? Boolean(this.getRootNode().#include));
344
344
  case 'accum':
345
345
  return this.#accum;
346
+ case 'built':
347
+ return this.#built;
346
348
  /* NOT FOR BROWSER */
347
349
  case 'stage':
348
350
  return this.#stage;
@@ -478,14 +480,16 @@ class Token extends element_1.AstElement {
478
480
  }
479
481
  /** @private */
480
482
  toString(separator) {
481
- const root = this.getRootNode();
483
+ const { rev } = debug_1.Shadow, root = this.getRootNode();
484
+ if (this.#string && this.#string[0] !== rev) {
485
+ this.#string = undefined;
486
+ }
482
487
  if (root.type === 'root'
483
488
  && root.#built
484
489
  && index_1.default.viewOnly) {
485
- this.#string ??= super.toString(separator);
486
- return this.#string;
490
+ this.#string ??= [rev, super.toString(separator)];
491
+ return this.#string[1];
487
492
  }
488
- this.#string = undefined;
489
493
  return super.toString(separator);
490
494
  }
491
495
  /* NOT FOR BROWSER */
@@ -64,6 +64,7 @@ class LinkBaseToken extends index_2.Token {
64
64
  this.#delimiter = this.buildFromStr(this.#delimiter, constants_1.BuildMethod.String);
65
65
  }
66
66
  this.setAttribute('name', this.#title.title);
67
+ super.afterBuild();
67
68
  /* NOT FOR BROWSER */
68
69
  const /** @implements */ linkListener = (e, data) => {
69
70
  const { prevTarget } = e;
@@ -183,7 +184,6 @@ class LinkBaseToken extends index_2.Token {
183
184
  const token = new this.constructor('', undefined, this.getAttribute('config'));
184
185
  token.firstChild.safeReplaceWith(link);
185
186
  token.append(...linkText);
186
- token.afterBuild();
187
187
  return token;
188
188
  });
189
189
  }
@@ -118,6 +118,7 @@ let GalleryImageToken = (() => {
118
118
  /** @private */
119
119
  afterBuild() {
120
120
  this.#setName(this.getTitle());
121
+ super.afterBuild();
121
122
  /* NOT FOR BROWSER */
122
123
  const /** @implements */ linkListener = (e, data) => {
123
124
  const { prevTarget } = e;
@@ -199,7 +199,6 @@ let MagicLinkToken = (() => {
199
199
  // @ts-expect-error abstract class
200
200
  const token = new MagicLinkToken(undefined, this.type, this.getAttribute('config'));
201
201
  token.append(...cloned);
202
- token.afterBuild();
203
202
  return token;
204
203
  });
205
204
  }
@@ -74,7 +74,6 @@ let NowikiBaseToken = (() => {
74
74
  return debug_1.Shadow.run(() => {
75
75
  const token = new constructor(data, this.getAttribute('config'));
76
76
  token.type = type;
77
- token.afterBuild();
78
77
  return token;
79
78
  });
80
79
  }
@@ -86,12 +86,8 @@ let DoubleUnderscoreToken = (() => {
86
86
  /** @override */
87
87
  cloneNode() {
88
88
  const config = this.getAttribute('config');
89
- return debug_1.Shadow.run(() => {
90
- // @ts-expect-error abstract class
91
- const token = new DoubleUnderscoreToken(this.innerText, this.#sensitive, config);
92
- token.afterBuild();
93
- return token;
94
- });
89
+ // @ts-expect-error abstract class
90
+ return debug_1.Shadow.run(() => new DoubleUnderscoreToken(this.innerText, this.#sensitive, config));
95
91
  }
96
92
  };
97
93
  return DoubleUnderscoreToken = _classThis;
@@ -129,6 +129,7 @@ let ParameterToken = (() => {
129
129
  parentNode.getAttribute('keys').add(name);
130
130
  }
131
131
  }
132
+ super.afterBuild();
132
133
  /* NOT FOR BROWSER */
133
134
  const /** @implements */ parameterListener = ({ prevTarget }, data) => {
134
135
  if (!this.anon) { // 匿名参数不管怎么变动还是匿名
@@ -192,7 +193,6 @@ let ParameterToken = (() => {
192
193
  const token = new ParameterToken(this.anon ? Number(this.name) : undefined, undefined, config);
193
194
  token.firstChild.safeReplaceWith(key);
194
195
  token.lastChild.safeReplaceWith(value);
195
- token.afterBuild();
196
196
  if (this.anon) {
197
197
  token.setAttribute('name', this.name);
198
198
  }
@@ -74,7 +74,6 @@ let SyntaxToken = (() => {
74
74
  return debug_1.Shadow.run(() => {
75
75
  const token = new SyntaxToken(undefined, this.pattern, this.type, config, [], acceptable);
76
76
  token.append(...cloned);
77
- token.afterBuild();
78
77
  return token;
79
78
  });
80
79
  }
@@ -3,10 +3,11 @@ import { Token } from '../index';
3
3
  import { SyntaxToken } from '../syntax';
4
4
  import { AttributesToken } from '../attributes';
5
5
  import type { AttributesParentBase } from '../../mixin/attributesParent';
6
+ declare type TableTypes = 'table' | 'tr' | 'td';
6
7
  export interface TableBaseToken extends AttributesParentBase {
7
8
  }
8
9
  export declare abstract class TableBaseToken extends Token {
9
- type: 'table' | 'tr' | 'td';
10
+ type: TableTypes;
10
11
  readonly childNodes: readonly [SyntaxToken, AttributesToken, ...Token[]];
11
12
  abstract get firstChild(): SyntaxToken;
12
13
  abstract get lastChild(): Token;
@@ -16,9 +17,10 @@ export declare abstract class TableBaseToken extends Token {
16
17
  /**
17
18
  * @param pattern 表格语法正则
18
19
  * @param syntax 表格语法
20
+ * @param type 节点类型
19
21
  * @param attr 表格属性
20
22
  */
21
- constructor(pattern: RegExp, syntax?: string, attr?: string, config?: Parser.Config, accum?: Token[], acceptable?: Acceptable);
23
+ constructor(pattern: RegExp, syntax: string, type: TableTypes, attr?: string, config?: Parser.Config, accum?: Token[], acceptable?: Acceptable);
22
24
  /** @override */
23
25
  cloneNode(): this;
24
26
  /** 转义表格语法 */
@@ -28,15 +28,16 @@ class TableBaseToken extends (0, attributesParent_1.attributesParent)(1)(index_2
28
28
  /**
29
29
  * @param pattern 表格语法正则
30
30
  * @param syntax 表格语法
31
+ * @param type 节点类型
31
32
  * @param attr 表格属性
32
33
  */
33
- constructor(pattern, syntax, attr, config = index_1.default.getConfig(), accum = [], acceptable = {}) {
34
+ constructor(pattern, syntax, type, attr, config = index_1.default.getConfig(), accum = [], acceptable = {}) {
34
35
  super(undefined, config, accum, acceptable);
35
36
  this.append(new syntax_1.SyntaxToken(syntax, pattern, 'table-syntax', config, accum, {
36
37
  'Stage-1': ':', '!ExtToken': '', TranscludeToken: ':',
37
38
  }),
38
39
  // @ts-expect-error abstract class
39
- new attributes_1.AttributesToken(attr, 'table-attrs', this.type, config, accum));
40
+ new attributes_1.AttributesToken(attr, 'table-attrs', type, config, accum));
40
41
  /* NOT FOR BROWSER */
41
42
  this.protectChildren(0, 1);
42
43
  }
@@ -65,7 +65,7 @@ class TableToken extends trBase_1.TrBaseToken {
65
65
  * @param attr 表格属性
66
66
  */
67
67
  constructor(syntax, attr, config, accum) {
68
- super(/^(?:\{\||\{\{\{\s*!\s*\}\}|\{\{\s*\(!\s*\}\})$/u, syntax, attr, config, accum, {
68
+ super(/^(?:\{\||\{\{\{\s*!\s*\}\}|\{\{\s*\(!\s*\}\})$/u, syntax, 'table', attr, config, accum, {
69
69
  Token: 2, SyntaxToken: [0, -1], AttributesToken: 1, TdToken: '2:', TrToken: '2:',
70
70
  });
71
71
  }
@@ -112,12 +112,15 @@ class TableToken extends trBase_1.TrBaseToken {
112
112
  close(syntax = '\n|}', halfParsed) {
113
113
  const config = this.getAttribute('config'), accum = this.getAttribute('accum'), inner = halfParsed ? [syntax] : index_1.default.parse(syntax, this.getAttribute('include'), 2, config).childNodes;
114
114
  if (this.lastChild.type !== 'table-syntax') {
115
- const token = debug_1.Shadow.run(() => super.insertAt(new syntax_1.SyntaxToken(undefined, closingPattern, 'table-syntax', config, accum, {
116
- 'Stage-1': ':', '!ExtToken': '', TranscludeToken: ':',
117
- })));
118
- if (!halfParsed) {
119
- token.afterBuild();
120
- }
115
+ debug_1.Shadow.run(() => {
116
+ const token = new syntax_1.SyntaxToken(undefined, closingPattern, 'table-syntax', config, accum, {
117
+ 'Stage-1': ':', '!ExtToken': '', TranscludeToken: ':',
118
+ });
119
+ super.insertAt(token);
120
+ if (!halfParsed) {
121
+ token.afterBuild();
122
+ }
123
+ });
121
124
  }
122
125
  this.lastChild.replaceChildren(...inner);
123
126
  }
@@ -108,7 +108,7 @@ let TdToken = (() => {
108
108
  innerSyntax = null;
109
109
  attr = '';
110
110
  }
111
- super(/^(?:\n[^\S\n]*(?:[|!]|\|\+|\{\{\s*!\s*\}\}\+?)|(?:\||\{\{\s*!\s*\}\}){2}|!!|\{\{\s*!!\s*\}\})$/u, syntax, attr, config, accum, { SyntaxToken: 0, AttributesToken: 1, Token: 2 });
111
+ super(/^(?:\n[^\S\n]*(?:[|!]|\|\+|\{\{\s*!\s*\}\}\+?)|(?:\||\{\{\s*!\s*\}\}){2}|!!|\{\{\s*!!\s*\}\})$/u, syntax, 'td', attr, config, accum, { SyntaxToken: 0, AttributesToken: 1, Token: 2 });
112
112
  if (innerSyntax) {
113
113
  [this.#innerSyntax] = innerSyntax;
114
114
  }
@@ -119,11 +119,14 @@ let TdToken = (() => {
119
119
  }
120
120
  /** 表格语法信息 */
121
121
  #getSyntax() {
122
+ const { rev } = debug_1.Shadow;
123
+ if (this.#syntax && this.#syntax[0] !== rev) {
124
+ this.#syntax = undefined;
125
+ }
122
126
  if (index_1.default.viewOnly) {
123
- this.#syntax ??= this.#computeSyntax();
124
- return this.#syntax;
127
+ this.#syntax ??= [rev, this.#computeSyntax()];
128
+ return this.#syntax[1];
125
129
  }
126
- this.#syntax = undefined;
127
130
  return this.#computeSyntax();
128
131
  }
129
132
  /** 表格语法信息 */
@@ -177,6 +180,7 @@ let TdToken = (() => {
177
180
  if (this.#innerSyntax.includes('\0')) {
178
181
  this.#innerSyntax = this.buildFromStr(this.#innerSyntax, constants_1.BuildMethod.String);
179
182
  }
183
+ super.afterBuild();
180
184
  }
181
185
  /** @private */
182
186
  toString() {
@@ -266,10 +270,6 @@ let TdToken = (() => {
266
270
  return token;
267
271
  }
268
272
  /** @private */
269
- getAttribute(key) {
270
- return key === 'innerSyntax' ? this.#innerSyntax : super.getAttribute(key);
271
- }
272
- /** @private */
273
273
  setAttribute(key, value) {
274
274
  if (key === 'innerSyntax') {
275
275
  this.#innerSyntax = (value ?? '');
@@ -16,7 +16,7 @@ class TrToken extends trBase_1.TrBaseToken {
16
16
  * @param attr 表格属性
17
17
  */
18
18
  constructor(syntax, attr, config, accum) {
19
- super(/^\n[^\S\n]*(?:\|-+|\{\{\s*!\s*\}\}-+|\{\{\s*!-\s*\}\}-*)$/u, syntax, attr, config, accum, {
19
+ super(/^\n[^\S\n]*(?:\|-+|\{\{\s*!\s*\}\}-+|\{\{\s*!-\s*\}\}-*)$/u, syntax, 'tr', attr, config, accum, {
20
20
  Token: 2, SyntaxToken: 0, AttributesToken: 1, TdToken: '2:',
21
21
  });
22
22
  }
@@ -157,11 +157,12 @@ class TranscludeToken extends index_2.Token {
157
157
  if (this.modifier.includes('\0')) {
158
158
  this.setAttribute('modifier', this.buildFromStr(this.modifier, constants_1.BuildMethod.String));
159
159
  }
160
+ super.afterBuild();
160
161
  /* NOT FOR BROWSER */
161
162
  if (this.isTemplate()) {
162
163
  const isTemplate = this.type === 'template';
163
- if (isTemplate || this.length > 1) {
164
- this.setAttribute(isTemplate ? 'name' : 'module', this.#getTitle().title);
164
+ if (isTemplate) {
165
+ this.setAttribute('name', this.#getTitle().title);
165
166
  }
166
167
  /**
167
168
  * 当事件bubble到`parameter`时,将`oldKey`和`newKey`保存进AstEventData。
@@ -174,9 +175,8 @@ class TranscludeToken extends index_2.Token {
174
175
  delete data.oldKey;
175
176
  delete data.newKey;
176
177
  }
177
- if (prevTarget === this.firstChild && isTemplate
178
- || prevTarget === this.childNodes[1] && !isTemplate && this.name === 'invoke') {
179
- this.setAttribute(isTemplate ? 'name' : 'module', this.#getTitle().title);
178
+ if (prevTarget === this.firstChild && isTemplate) {
179
+ this.setAttribute('name', this.#getTitle().title);
180
180
  }
181
181
  else if (oldKey !== newKey && prevTarget instanceof parameter_1.ParameterToken) {
182
182
  const oldArgs = this.getArgs(oldKey, false, false);
@@ -212,8 +212,6 @@ class TranscludeToken extends index_2.Token {
212
212
  case 'padding':
213
213
  return this.modifier.length + 2;
214
214
  /* NOT FOR BROWSER */
215
- case 'args':
216
- return this.#args;
217
215
  case 'keys':
218
216
  return this.#keys;
219
217
  /* NOT FOR BROWSER END */
@@ -409,7 +407,6 @@ class TranscludeToken extends index_2.Token {
409
407
  token.setAttribute('modifier', this.modifier);
410
408
  }
411
409
  token.firstChild.safeReplaceWith(first);
412
- token.afterBuild();
413
410
  token.append(...cloned);
414
411
  return token;
415
412
  });
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.aliases = exports.parsers = exports.mixins = exports.classes = exports.BuildMethod = exports.MAX_STAGE = void 0;
3
+ exports.aliases = exports.constants = exports.parsers = exports.mixins = exports.classes = exports.BuildMethod = exports.MAX_STAGE = void 0;
4
4
  exports.MAX_STAGE = 11;
5
5
  var BuildMethod;
6
6
  (function (BuildMethod) {
@@ -8,9 +8,7 @@ var BuildMethod;
8
8
  BuildMethod[BuildMethod["Text"] = 1] = "Text";
9
9
  })(BuildMethod || (exports.BuildMethod = BuildMethod = {}));
10
10
  /* NOT FOR BROWSER */
11
- exports.classes = {};
12
- exports.mixins = {};
13
- exports.parsers = {};
11
+ exports.classes = {}, exports.mixins = {}, exports.parsers = {}, exports.constants = {};
14
12
  exports.aliases = [
15
13
  ['AstText'],
16
14
  ['CommentToken', 'ExtToken', 'IncludeToken', 'NoincludeToken'],
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.undo = exports.mixin = exports.emptyArray = exports.setChildNodes = exports.isToken = exports.Shadow = void 0;
4
+ const constants_1 = require("./constants");
4
5
  exports.Shadow = {
5
6
  running: false,
6
7
  /** @private */
@@ -8,7 +9,11 @@ exports.Shadow = {
8
9
  const { running } = this;
9
10
  this.running = true;
10
11
  try {
12
+ const { Token: AnyToken } = require('../src/index');
11
13
  const result = callback();
14
+ if (result instanceof AnyToken && !result.getAttribute('built')) {
15
+ result.afterBuild();
16
+ }
12
17
  this.running = running;
13
18
  return result;
14
19
  }
@@ -17,6 +22,7 @@ exports.Shadow = {
17
22
  throw e;
18
23
  }
19
24
  },
25
+ rev: 0,
20
26
  };
21
27
  /**
22
28
  * 是否是某一特定类型的节点
@@ -88,3 +94,4 @@ const undo = (e, data) => {
88
94
  }
89
95
  };
90
96
  exports.undo = undo;
97
+ constants_1.constants['Shadow'] = __filename;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikiparser-node",
3
- "version": "1.9.1",
3
+ "version": "1.9.2",
4
4
  "description": "A Node.js parser for MediaWiki markup with AST",
5
5
  "keywords": [
6
6
  "mediawiki",
package/README.en.md DELETED
@@ -1,57 +0,0 @@
1
- [![npm version](https://badge.fury.io/js/wikiparser-node.svg)](https://www.npmjs.com/package/wikiparser-node)
2
- [![CodeQL](https://github.com/bhsd-harry/wikiparser-node/actions/workflows/codeql.yml/badge.svg)](https://github.com/bhsd-harry/wikiparser-node/actions/workflows/codeql.yml)
3
- [![CI](https://github.com/bhsd-harry/wikiparser-node/actions/workflows/node.js.yml/badge.svg)](https://github.com/bhsd-harry/wikiparser-node/actions/workflows/node.js.yml)
4
-
5
- # Other Languages
6
-
7
- - [简体中文](./README.md)
8
-
9
- # Introduction
10
-
11
- WikiParser-Node is an offline [Wikitext](https://www.mediawiki.org/wiki/Wikitext) parser developed by Bhsd for the [Node.js](https://nodejs.org/) environment. It can parse almost all wiki syntax and generate an [Abstract Syntax Tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree) ([Try it online](https://bhsd-harry.github.io/wikiparser-node/#editor)). It also allows for easy querying and modification of the AST, and returns the modified wikitext.
12
-
13
- # Other Versions
14
-
15
- ## Mini (also known as [WikiLint](https://www.npmjs.com/package/wikilint))
16
-
17
- This version provides a [CLI](https://en.wikipedia.org/wiki/Command-line_interface), but only retains the parsing functionality and linting functionality. The parsed AST cannot be modified. It is used in the [eslint-plugin-wikitext](https://www.npmjs.com/package/eslint-plugin-wikitext) plugin.
18
-
19
- ## Browser-compatible
20
-
21
- A browser-compatible version, which can be used for code highlighting or as a linting plugin in conjunction with editors such as [CodeMirror](https://codemirror.net/) and [Monaco](https://microsoft.github.io/monaco-editor/). ([Usage example](https://bhsd-harry.github.io/wikiparser-node))
22
-
23
- # Installation
24
-
25
- ## Node.js
26
-
27
- Please install the corresponding version as needed (`WikiParser-Node` or `WikiLint`), for example:
28
-
29
- ```sh
30
- npm i wikiparser-node
31
- ```
32
-
33
- or
34
-
35
- ```sh
36
- npm i wikilint
37
- ```
38
-
39
- ## Browser
40
-
41
- You can download the code via CDN, for example:
42
-
43
- ```html
44
- <script src="//cdn.jsdelivr.net/npm/wikiparser-node@browser/bundle/bundle.min.js"></script>
45
- ```
46
-
47
- or
48
-
49
- ```html
50
- <script src="//unpkg.com/wikiparser-node@browser/bundle/bundle.min.js"></script>
51
- ```
52
-
53
- For more browser extensions, please refer to the corresponding [documentation](https://github.com/bhsd-harry/wikiparser-node/wiki/Browser-%28EN%29).
54
-
55
- # Usage
56
-
57
- Please refer to the [Wiki](https://github.com/bhsd-harry/wikiparser-node/wiki/Home-%28EN%29).