wikiparser-node 0.0.2 → 0.2.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 (49) hide show
  1. package/README.md +226 -7
  2. package/config/default.json +12 -1
  3. package/config/llwiki.json +12 -1
  4. package/config/moegirl.json +9 -1
  5. package/errors/2022-07-04T22:30:41.785Z +1 -0
  6. package/errors/2022-07-04T22:30:41.785Z.err +11 -0
  7. package/errors/2022-07-04T22:30:41.785Z.json +5 -0
  8. package/errors/README +1 -0
  9. package/index.js +85 -9
  10. package/lib/element.js +72 -13
  11. package/lib/node.js +17 -9
  12. package/mixin/sol.js +42 -0
  13. package/package.json +1 -1
  14. package/parser/converter.js +44 -0
  15. package/parser/externalLinks.js +1 -1
  16. package/parser/list.js +58 -0
  17. package/parser/table.js +2 -2
  18. package/printed/README +1 -0
  19. package/src/arg.js +9 -9
  20. package/src/attribute.js +33 -23
  21. package/src/converter.js +135 -0
  22. package/src/converterFlags.js +214 -0
  23. package/src/converterRule.js +209 -0
  24. package/src/extLink.js +23 -10
  25. package/src/heading.js +15 -20
  26. package/src/html.js +4 -3
  27. package/src/imageParameter.js +6 -7
  28. package/src/index.js +38 -23
  29. package/src/link/file.js +9 -9
  30. package/src/link/index.js +9 -11
  31. package/src/magicLink.js +2 -3
  32. package/src/nowiki/comment.js +1 -1
  33. package/src/nowiki/dd.js +49 -0
  34. package/src/nowiki/hr.js +3 -2
  35. package/src/nowiki/list.js +16 -0
  36. package/src/parameter.js +5 -5
  37. package/src/syntax.js +3 -1
  38. package/src/table/index.js +35 -30
  39. package/src/table/td.js +3 -2
  40. package/src/table/tr.js +6 -12
  41. package/src/tagPair/index.js +1 -1
  42. package/src/transclude.js +28 -25
  43. package/tool/index.js +50 -40
  44. package/typings/index.d.ts +3 -0
  45. package/typings/node.d.ts +3 -3
  46. package/typings/token.d.ts +1 -0
  47. package/util/debug.js +3 -3
  48. package/util/string.js +16 -1
  49. package/src/listToken.js +0 -47
package/README.md CHANGED
@@ -29,6 +29,7 @@
29
29
  10. [findEnclosingHtml](#token.findenclosinghtml)
30
30
  11. [getCategories](#token.getcategories)
31
31
  12. [redoQuotes](#token.redoquotes)
32
+ 13. [print](#token.print)
32
33
  2. [实例属性](#token.instance.properties)
33
34
  1. [type](#token.type)
34
35
  3. [原型属性](#token.prototype.properties)
@@ -180,12 +181,35 @@
180
181
  2. [setTarget](#magiclinktoken.settarget)
181
182
  2. [原型属性](#magiclinktoken.prototype.properties)
182
183
  1. [protocol](#magiclinktoken.protocol)
183
- 21. [选择器](#选择器)
184
+ 21. [ConverterToken](#convertertoken)
185
+ 1. [原型方法](#convertertoken.prototype.methods)
186
+ 1. [getAllFlags](#convertertoken.getallflags)
187
+ 2. [getEffectiveFlags](#convertertoken.geteffectiveflags)
188
+ 3. [hasFlag](#convertertoken.hasflag)
189
+ 4. [hasEffectiveFlag](#convertertoken.haseffectiveflag)
190
+ 5. [removeFlag](#convertertoken.removeflag)
191
+ 6. [setFlag](#convertertoken.setflag)
192
+ 7. [toggleFlag](#convertertoken.toggleflag)
193
+ 2. [原型属性](#convertertoken.prototype.properties)
194
+ 1. [noConvert](#convertertoken.noconvert)
195
+ 22. [ConverterRuleToken](#converterruletoken)
196
+ 1. [原型方法](#converterruletoken.prototype.methods)
197
+ 1. [noConvert](#converterruletoken.noconvert)
198
+ 2. [setTo](#converterruletoken.setto)
199
+ 3. [setVariant](#converterruletoken.setvariant)
200
+ 4. [setFrom](#converterruletoken.setfrom)
201
+ 5. [makeUnidirectional](#converterruletoken.makeunidirectional)
202
+ 6. [makeBidirectional](#converterruletoken.makebidirectional)
203
+ 2. [实例属性](#converterruletoken.instance.properties)
204
+ 1. [variant](#converterruletoken.variant)
205
+ 2. [unidirectional](#converterruletoken.unidirectional)
206
+ 3. [bidirectional](#converterruletoken.bidirectional)
207
+ 23. [选择器](#选择器)
184
208
  1. [type](#selector.type)
185
209
  2. [name](#selector.name)
186
210
  3. [属性](#selector.attribute)
187
211
  4. [伪选择器](#selector.pseudo)
188
- 22. [$ (TokenCollection)](#-tokencollection)
212
+ 24. [$ (TokenCollection)](#-tokencollection)
189
213
  </details>
190
214
 
191
215
  # Parser
@@ -426,6 +450,15 @@ assert.deepStrictEqual(root.childNodes, ["'", root.firstElementChild, "a", root.
426
450
  var root = Parser.parse(wikitext);
427
451
  assert(root.type === 'root');
428
452
  ```
453
+
454
+ **print**(format?: 'markup'\|'json' = 'markup'): void\|object<a id="token.print"></a>
455
+ - 打印解析生成的 AST。
456
+
457
+ ```js
458
+ var root = Parser.parse("<ref>{{T|<br>\n----\n[[File:F|thumb|''[//example.net]'']]}}</ref>");
459
+ root.print();
460
+ root.print('json', 'example'); // JSON格式的输出结果将保存至 /printed/example.json 文件
461
+ ```
429
462
  </details>
430
463
 
431
464
  ## 原型属性<a id="token.prototype.properties"></a>
@@ -930,7 +963,7 @@ param.setValue(' 2 ');
930
963
  assert(root.toString() === '{{a|b= 2 }}'); // setValue方法总是保留空白字符,哪怕是无效的
931
964
  ```
932
965
 
933
- **rename**(key: string, force: boolean): void<a id="parametertoken.rename"></a>
966
+ **rename**(key: string, force?: boolean = false): void<a id="parametertoken.rename"></a>
934
967
  - 重命名参数,可选是否在导致重复参数时抛出错误。
935
968
 
936
969
  ```js
@@ -1164,7 +1197,7 @@ assert(root.toString() === '{|\n!colspan=2|\n|-\n| \n!\n|}');
1164
1197
  |:-:|:-:|:-:|
1165
1198
  |<table><tr><td colspan=2>td</td></tr><tr><td>td</td><td>td</td></tr></table>|<table><tr><td colspan=2>td</td></tr><tr><td>td</td><th>th</th></tr></table>|<table><tr><th colspan=2>th</th></tr><tr><td>td</td><th>th</th></tr></table>|
1166
1199
 
1167
- **insertTableRow**(row: number, attr: Record\<string, string\|boolean>, inner?: string, subtype?: 'td'\|'th', innerAttr?: Record\<string, string\|boolean>): TrToken<a id="tabletoken.inserttablerow"></a>
1200
+ **insertTableRow**(row: number, attr: Record\<string, string\|boolean>, inner?: string, subtype?: 'td'\|'th' = 'td', innerAttr?: Record\<string, string\|boolean>): TrToken<a id="tabletoken.inserttablerow"></a>
1168
1201
  - 插入空行或一行单元格。
1169
1202
 
1170
1203
  ```js
@@ -1179,7 +1212,7 @@ assert(root.toString() === '{|\n|a|| rowspan="3"|b||c\n|- class="tr"\n|-\n! clas
1179
1212
  |:-:|:-:|
1180
1213
  |<table><tr><td>a</td><td rowspan=2>b</td><td>c</td></tr><tr><td>d</td><td>e</td></tr></table>|<table><tr><td>a</td><td rowspan=3>b</td><td>c</td></tr><tr><th>f</th><th>f</th></tr><tr><td>d</td><td>e</td></tr></table>|
1181
1214
 
1182
- **insertTableCol**(x: number, inner: string, subtype: 'td'\|'th', attr: Record\<string, string\|boolean>): void<a id="tabletoken.inserttablecol"></a>
1215
+ **insertTableCol**(x: number, inner: string, subtype?: 'td'\|'th' = 'td', attr?: Record\<string, string\|boolean>): void<a id="tabletoken.inserttablecol"></a>
1183
1216
  - 插入一列单元格。
1184
1217
 
1185
1218
  ```js
@@ -1193,7 +1226,7 @@ assert(root.toString() === '{|\n| colspan="3"|a\n|-\n|b\n! class="th"|d\n|c\n|}'
1193
1226
  |:-:|:-:|
1194
1227
  |<table><tr><td colspan=2 align="center">a</td></tr><tr><td>b</td><td>c</td></tr></table>|<table><tr><td colspan=3 align="center">a</td></tr><tr><td>b</td><th>d</th><td>c</td></tr></table>|
1195
1228
 
1196
- **insertTableCell**(inner: string, coords: {row: number, column: number}\|{x: number, y: number}, subtype: 'td'\|'th', attr: Record\<string, string\|boolean>): [TdToken](#tdtoken)<a id="tabletoken.inserttablecell"></a>
1229
+ **insertTableCell**(inner: string, coords: {row: number, column: number}\|{x: number, y: number}, subtype?: 'td'\|'th' = 'td', attr?: Record\<string, string\|boolean>): [TdToken](#tdtoken)<a id="tabletoken.inserttablecell"></a>
1197
1230
  - 插入一个单元格。
1198
1231
 
1199
1232
  ```js
@@ -1471,7 +1504,7 @@ link.asSelfLink();
1471
1504
  assert(root.toString() === '[[#b]]');
1472
1505
  ```
1473
1506
 
1474
- **setLinkText**(linkText?: string)<a id="linktoken.setlinktext"></a>
1507
+ **setLinkText**(linkText: string)<a id="linktoken.setlinktext"></a>
1475
1508
  - 修改链接文本。
1476
1509
 
1477
1510
  ```js
@@ -1797,6 +1830,192 @@ assert(root.toString() === 'https://example.org');
1797
1830
 
1798
1831
  [返回目录](#目录)
1799
1832
 
1833
+ # ConverterToken
1834
+ 转换。
1835
+
1836
+ ## 原型方法<a id="convertertoken.prototype.methods"></a>
1837
+ <details>
1838
+ <summary>展开</summary>
1839
+
1840
+ **getAllFlags**(): Set\<string><a id="convertertoken.getallflags"></a>
1841
+ - 获取所有转换 flag,包括无效的。
1842
+
1843
+ ```js
1844
+ var root = Parser.parse('-{zh-hans;zh-hant|}-'),
1845
+ converter = root.firstChild;
1846
+ assert.deepStrictEqual(converter.getAllFlags(), new Set(['zh-hans', 'zh-hant']));
1847
+ ```
1848
+
1849
+ **getEffectiveFlags**(): Set\<string><a id="convertertoken.geteffectiveflags"></a>
1850
+ - 解析有效的转换 flag。
1851
+
1852
+ ```js
1853
+ var root = Parser.parse('-{}-'),
1854
+ converter = root.firstChild;
1855
+ assert.deepStrictEqual(converter.getEffectiveFlags(), new Set(['S']));
1856
+ ```
1857
+
1858
+ **hasFlag**(flag: string): boolean<a id="convertertoken.hasflag"></a>
1859
+ - 是否包含 flag,即使可能是无效 flag。
1860
+
1861
+ ```js
1862
+ var root = Parser.parse('-{R|}-'),
1863
+ converter = root.firstChild;
1864
+ assert(converter.hasFlag('R') === true);
1865
+ ```
1866
+
1867
+ **hasEffectiveFlag**(flag: string): boolean<a id="convertertoken.haseffectiveflag"></a>
1868
+ - 是否包含某有效 flag。
1869
+
1870
+ ```js
1871
+ var root = Parser.parse('-{}-'),
1872
+ converter = root.firstChild;
1873
+ assert(converter.hasEffectiveFlag('S') === true);
1874
+ ```
1875
+
1876
+ **removeFlag**(flag: string): void<a id="convertertoken.removeflag"></a>
1877
+ - 移除 flag,可能原本就是无效的 flag。
1878
+
1879
+ ```js
1880
+ var root = Parser.parse('-{R|}-'),
1881
+ converter = root.firstChild;
1882
+ converter.removeFlag('R');
1883
+ assert(root.toString() === '-{}-');
1884
+ ```
1885
+
1886
+ **setFlag**(flag: string): void<a id="convertertoken.setflag"></a>
1887
+ - 添加指定 flag,可能是无效 flag。
1888
+
1889
+ ```js
1890
+ var root = Parser.parse('-{}-'),
1891
+ converter = root.firstChild;
1892
+ converter.setFlag('R');
1893
+ assert(root.toString() === '-{R|}-');
1894
+ ```
1895
+
1896
+ **toggleFlag**(flag: string): void<a id="convertertoken.toggleflag"></a>
1897
+ - 改变某 flag 的有无,可能是无效 flag。
1898
+
1899
+ ```js
1900
+ var root = Parser.parse('-{R|}-'),
1901
+ converter = root.firstChild;
1902
+ converter.toggleFlag('R');
1903
+ converter.toggleFlag('D');
1904
+ assert(root.toString() === '-{D|}-');
1905
+ ```
1906
+ </details>
1907
+
1908
+ ## 原型属性<a id="convertertoken.prototype.properties"></a>
1909
+ <details>
1910
+ <summary>展开</summary>
1911
+
1912
+ **noConvert**: boolean<a id="convertertoken.noconvert"></a>
1913
+ - 是否不会转换。
1914
+
1915
+ ```js
1916
+ var root = Parser.parse('-{简体}-'),
1917
+ converter = root.firstChild;
1918
+ assert(converter.noConvert === true);
1919
+ ```
1920
+ </details>
1921
+
1922
+ [返回目录](#目录)
1923
+
1924
+ # ConverterRuleToken
1925
+ 单条转换规则。
1926
+
1927
+ ## 原型方法<a id="converterruletoken.prototype.methods"></a>
1928
+ <details>
1929
+ <summary>展开</summary>
1930
+
1931
+ **noConvert**(): void<a id="converterruletoken.noconvert"></a>
1932
+ - 更改规则使得内容不会转换。
1933
+
1934
+ ```js
1935
+ var root = Parser.parse('-{繁體=>zh-hans:繁體}-'),
1936
+ converterRule = root.querySelector('converter-rule');
1937
+ converterRule.noConvert();
1938
+ assert(root.toString() === '-{繁體}-');
1939
+ ```
1940
+
1941
+ **setTo**(to: string): void<a id="converterruletoken.setto"></a>
1942
+ - 修改转换结果。
1943
+
1944
+ ```js
1945
+ var root = Parser.parse('-{繁體=>zh-hans:繁體}-'),
1946
+ converterRule = root.querySelector('converter-rule');
1947
+ converterRule.setTo('繁体');
1948
+ assert(root.toString() === '-{繁體=>zh-hans:繁体}-');
1949
+ ```
1950
+
1951
+ **setVariant**(variant: string): void<a id="converterruletoken.setvariant"></a>
1952
+ - 修改转换规则对应的语言变体。
1953
+
1954
+ ```js
1955
+ var root = Parser.parse('-{繁體=>zh-hans:繁體}-'),
1956
+ converterRule = root.querySelector('converter-rule');
1957
+ converterRule.setVariant('zh-cn');
1958
+ assert(root.toString() === '-{繁體=>zh-cn:繁體}-');
1959
+ ```
1960
+
1961
+ **setFrom**(from: string): void<a id="converterruletoken.setfrom"></a>
1962
+ - 对单项转换规则,修改待转换的内容。
1963
+
1964
+ ```js
1965
+ var root = Parser.parse('-{繁體=>zh-hans:繁体}-'),
1966
+ converterRule = root.querySelector('converter-rule');
1967
+ converterRule.setFrom('正體');
1968
+ assert(root.toString() === '-{正體=>zh-hans:繁体}-');
1969
+ ```
1970
+
1971
+ **makeUnidirectional**(from: string): void<a id="converterruletoken.makeunidirectional"></a>
1972
+ - [ConverterRuleToken.setFrom](#converterruletoken.setfrom) 方法的別名。
1973
+
1974
+ **makeBidirectional**(): void<a id="converterruletoken.makebidirectional"></a>
1975
+ - 将规则改为双向转换。
1976
+
1977
+ ```js
1978
+ var root = Parser.parse('-{繁體=>zh-hans:繁体}-'),
1979
+ converterRule = root.querySelector('converter-rule');
1980
+ converterRule.makeBidirectional();
1981
+ assert(root.toString() === '-{zh-hans:繁体}-');
1982
+ ```
1983
+ </details>
1984
+
1985
+ ## 原型属性<a id="convertertoken.prototype.properties"></a>
1986
+ <details>
1987
+ <summary>展开</summary>
1988
+
1989
+ **variant**: string<a id="converterruletoken.variant"></a>
1990
+ - 规则对应的语言变体。
1991
+
1992
+ ```js
1993
+ var root = Parser.parse('-{繁體=>zh-hans:繁体}-'),
1994
+ converterRule = root.querySelector('converter-rule');
1995
+ assert(converterRule.variant === 'zh-hans');
1996
+ ```
1997
+
1998
+ **unidirectional**: boolean<a id="converterruletoken.unidirectional"></a>
1999
+ - 是否是单向转换。
2000
+
2001
+ ```js
2002
+ var root = Parser.parse('-{繁體=>zh-hans:繁体}-'),
2003
+ converterRule = root.querySelector('converter-rule');
2004
+ assert(converterRule.unidirectional === true);
2005
+ ```
2006
+
2007
+ **bidirectional**: boolean<a id="converterruletoken.bidirectional"></a>
2008
+ - 是否是双向转换。
2009
+
2010
+ ```js
2011
+ var root = Parser.parse('-{zh-hans:繁体}-'),
2012
+ converterRule = root.querySelector('converter-rule');
2013
+ assert(converterRule.bidirectional === true);
2014
+ ```
2015
+ </details>
2016
+
2017
+ [返回目录](#目录)
2018
+
1800
2019
  # 选择器
1801
2020
  Token 选择器的设计仿照了 CSS 和 jQuery 的选择器。
1802
2021
  <details>
@@ -763,5 +763,16 @@
763
763
  "底部": "bottom",
764
764
  "text-bottom": "text-bottom",
765
765
  "文字底部": "text-bottom"
766
- }
766
+ },
767
+ "variants": [
768
+ "zh",
769
+ "zh-hans",
770
+ "zh-hant",
771
+ "zh-cn",
772
+ "zh-tw",
773
+ "zh-hk",
774
+ "zh-sg",
775
+ "zh-my",
776
+ "zh-mo"
777
+ ]
767
778
  }
@@ -683,5 +683,16 @@
683
683
  "底部": "bottom",
684
684
  "text-bottom": "text-bottom",
685
685
  "文字底部": "text-bottom"
686
- }
686
+ },
687
+ "variants": [
688
+ "zh",
689
+ "zh-hans",
690
+ "zh-hant",
691
+ "zh-cn",
692
+ "zh-tw",
693
+ "zh-hk",
694
+ "zh-sg",
695
+ "zh-my",
696
+ "zh-mo"
697
+ ]
687
698
  }
@@ -718,5 +718,13 @@
718
718
  "底部": "bottom",
719
719
  "text-bottom": "text-bottom",
720
720
  "文字底部": "text-bottom"
721
- }
721
+ },
722
+ "variants": [
723
+ "zh",
724
+ "zh-hans",
725
+ "zh-hant",
726
+ "zh-cn",
727
+ "zh-tw",
728
+ "zh-hk"
729
+ ]
722
730
  }
@@ -0,0 +1 @@
1
+ {|
@@ -0,0 +1,11 @@
1
+ TypeError: normalizeSpace is not a function
2
+ at AttributeToken.toString (/Users/mengxiwu/Documents/CCM-RP/node/wikiparser-node/src/attribute.js:249:44)
3
+ at #parseAttr (/Users/mengxiwu/Documents/CCM-RP/node/wikiparser-node/src/attribute.js:58:21)
4
+ at new AttributeToken (/Users/mengxiwu/Documents/CCM-RP/node/wikiparser-node/src/attribute.js:87:45)
5
+ at new TrToken (/Users/mengxiwu/Documents/CCM-RP/node/wikiparser-node/src/table/tr.js:28:4)
6
+ at new TableToken (/Users/mengxiwu/Documents/CCM-RP/node/wikiparser-node/src/table/index.js:66:3)
7
+ at parseTable (/Users/mengxiwu/Documents/CCM-RP/node/wikiparser-node/parser/table.js:40:18)
8
+ at #parseTable (/Users/mengxiwu/Documents/CCM-RP/node/wikiparser-node/src/index.js:553:16)
9
+ at Token.parseOnce (/Users/mengxiwu/Documents/CCM-RP/node/wikiparser-node/src/index.js:431:21)
10
+ at Token.parse (/Users/mengxiwu/Documents/CCM-RP/node/wikiparser-node/src/index.js:526:9)
11
+ at /Users/mengxiwu/Documents/CCM-RP/node/wikiparser-node/index.js:134:11
@@ -0,0 +1,5 @@
1
+ {
2
+ "stage": 3,
3
+ "include": false,
4
+ "config": "./config/default"
5
+ }
package/errors/README ADDED
@@ -0,0 +1 @@
1
+ 这里记录解析失败时处于半解析状态的维基文本以及错误信息。
package/index.js CHANGED
@@ -76,6 +76,8 @@ const /** @type {Parser} */ Parser = {
76
76
  ['QuoteToken'],
77
77
  ['ExtLinkToken'],
78
78
  ['MagicLinkToken'],
79
+ ['ListToken', 'DdToken'],
80
+ ['ConverterToken'],
79
81
  ],
80
82
 
81
83
  config: './config/default',
@@ -108,7 +110,7 @@ const /** @type {Parser} */ Parser = {
108
110
  }
109
111
  }
110
112
  };
111
- Parser.run(() => {
113
+ this.run(() => {
112
114
  build(['title', 'fragment']);
113
115
  });
114
116
  }
@@ -119,31 +121,105 @@ const /** @type {Parser} */ Parser = {
119
121
 
120
122
  parse(wikitext, include = false, maxStage = Parser.MAX_STAGE, config = Parser.getConfig()) {
121
123
  const Token = require('./src');
124
+ let token;
122
125
  this.run(() => {
123
126
  if (typeof wikitext === 'string') {
124
- wikitext = new Token(wikitext, config);
125
- } else if (!(wikitext instanceof Token)) {
127
+ token = new Token(wikitext, config);
128
+ } else if (wikitext instanceof Token) {
129
+ token = wikitext;
130
+ wikitext = token.toString();
131
+ } else {
126
132
  throw new TypeError('待解析的内容应为 String 或 Token!');
127
133
  }
128
134
  try {
129
- wikitext.parse(maxStage, include);
135
+ token.parse(maxStage, include);
130
136
  } catch (e) {
131
137
  if (e instanceof Error) {
132
- fs.writeFileSync(
133
- `${__dirname}/errors/${new Date().toISOString()}`,
134
- `${e.stack}\n\n\n${wikitext.toString()}`,
135
- );
138
+ const file = `${__dirname}/errors/${new Date().toISOString()}`,
139
+ stage = token.getAttribute('stage');
140
+ fs.writeFileSync(file, stage === this.MAX_STAGE ? wikitext : token.toString());
141
+ fs.writeFileSync(`${file}.err`, e.stack);
142
+ fs.writeFileSync(`${file}.json`, JSON.stringify({
143
+ stage, include: token.getAttribute('include'), config: this.config,
144
+ }, null, '\t'));
136
145
  }
137
146
  throw e;
138
147
  }
139
148
  });
140
- return wikitext;
149
+ return token;
150
+ },
151
+
152
+ reparse(date) {
153
+ const path = `${__dirname}/errors/`,
154
+ main = fs.readdirSync(path).find(name => name.startsWith(date) && name.endsWith('Z'));
155
+ if (!main) {
156
+ throw new RangeError(`找不到对应时间戳的错误记录:${date}`);
157
+ }
158
+ const Token = require('./src'),
159
+ file = `${path}${main}`,
160
+ wikitext = fs.readFileSync(file, 'utf8'),
161
+ {stage, include, config} = require(`${file}.json`);
162
+ this.config = config;
163
+ return this.run(() => {
164
+ const halfParsed = stage < this.MAX_STAGE,
165
+ token = new Token(wikitext, this.getConfig(), halfParsed);
166
+ if (halfParsed) {
167
+ token.setAttribute('stage', stage).parseOnce(stage, include);
168
+ } else {
169
+ token.parse(undefined, include);
170
+ }
171
+ fs.unlinkSync(file);
172
+ fs.unlinkSync(`${file}.err`);
173
+ fs.unlinkSync(`${file}.json`);
174
+ return token;
175
+ });
141
176
  },
142
177
 
143
178
  getTool() {
144
179
  delete require.cache[require.resolve('./tool')];
145
180
  return require('./tool');
146
181
  },
182
+
183
+ typeAliases: {
184
+ ext: ['extension'],
185
+ 'ext-inner': ['extension-inner'],
186
+ arg: ['argument'],
187
+ 'arg-name': ['argument-name'],
188
+ 'arg-default': ['argument-default'],
189
+ 'arg-redundant': ['argument-redundant'],
190
+ template: ['tpl'],
191
+ 'template-name': ['tpl-name'],
192
+ 'magic-word': ['parser-function', 'parser-func'],
193
+ 'invoke-function': ['invoke-func'],
194
+ 'invoke-module': ['invoke-mod'],
195
+ parameter: ['param'],
196
+ 'parameter-key': ['param-key'],
197
+ 'parameter-value': ['parameter-val', 'param-value', 'param-val'],
198
+ heading: ['header'],
199
+ 'heading-title': ['header-title'],
200
+ table: ['tbl'],
201
+ 'table-inter': ['tbl-inter'],
202
+ tr: ['table-row', 'tbl-row'],
203
+ td: ['table-cell', 'tbl-cell', 'table-data', 'tbl-data'],
204
+ 'double-underscore': ['underscore', 'behavior-switch', 'behaviour-switch'],
205
+ hr: ['horizontal'],
206
+ category: ['category-link', 'cat', 'cat-link'],
207
+ file: ['file-link', 'image', 'image-link', 'img', 'img-link'],
208
+ 'image-parameter': ['img-parameter', 'image-param', 'img-param'],
209
+ quote: ['quotes', 'quot', 'apostrophe', 'apostrophes', 'apos'],
210
+ 'ext-link': ['external-link'],
211
+ 'ext-link-text': ['external-link-text'],
212
+ 'ext-link-url': ['external-link-url'],
213
+ 'free-ext-link': ['free-external-link', 'magic-link'],
214
+ dd: ['indent', 'indentation'],
215
+ converter: ['convert', 'conversion'],
216
+ 'converter-flags': ['convert-flags', 'conversion-flags', 'converter-flag', 'convert-flag', 'conversion-flag'],
217
+ 'converter-rule': ['convert-rule', 'conversion-rule'],
218
+ 'converter-rule-noconvert': ['convert-rule-noconvert', 'conversion-rule-noconvert'],
219
+ 'converter-rule-variant': ['convert-rule-variant', 'conversion-rule-variant'],
220
+ 'converter-rule-to': ['convert-rule-to', 'conversion-rule-to'],
221
+ 'converter-rule-from': ['convert-rule-from', 'conversion-rule-from'],
222
+ },
147
223
  };
148
224
 
149
225
  const /** @type {PropertyDescriptorMap} */ def = {};
package/lib/element.js CHANGED
@@ -1,7 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const {typeError, externalUse} = require('../util/debug'),
4
- {toCase} = require('../util/string'),
3
+ const fs = require('fs'),
4
+ {externalUse} = require('../util/debug'),
5
+ {toCase, noWrap} = require('../util/string'),
5
6
  {nth} = require('./ranges'),
6
7
  EventEmitter = require('events'),
7
8
  AstNode = require('./node'),
@@ -93,7 +94,7 @@ class AstElement extends AstNode {
93
94
  this.addEventListener(type, listener, options);
94
95
  }
95
96
  } else if (typeof types !== 'string' || typeof listener !== 'function') {
96
- typeError(this, 'addEventListener', 'String', 'Function');
97
+ this.typeError('addEventListener', 'String', 'Function');
97
98
  } else {
98
99
  this.#events[options?.once ? 'once' : 'on'](types, listener);
99
100
  }
@@ -109,7 +110,7 @@ class AstElement extends AstNode {
109
110
  this.removeEventListener(type, listener);
110
111
  }
111
112
  } else if (typeof types !== 'string' || typeof listener !== 'function') {
112
- typeError(this, 'removeEventListener', 'String', 'Function');
113
+ this.typeError('removeEventListener', 'String', 'Function');
113
114
  } else {
114
115
  this.#events.off(types, listener);
115
116
  }
@@ -122,7 +123,7 @@ class AstElement extends AstNode {
122
123
  this.removeAllEventListeners(type);
123
124
  }
124
125
  } else if (types !== undefined && typeof types !== 'string') {
125
- typeError(this, 'removeAllEventListeners', 'String');
126
+ this.typeError('removeAllEventListeners', 'String');
126
127
  } else {
127
128
  this.#events.removeAllListeners(types);
128
129
  }
@@ -134,7 +135,7 @@ class AstElement extends AstNode {
134
135
  */
135
136
  listEventListeners(type) {
136
137
  if (typeof type !== 'string') {
137
- typeError(this, 'listEventListeners', 'String');
138
+ this.typeError('listEventListeners', 'String');
138
139
  }
139
140
  return this.#events.listeners(type);
140
141
  }
@@ -145,7 +146,7 @@ class AstElement extends AstNode {
145
146
  */
146
147
  dispatchEvent(e, data) {
147
148
  if (!(e instanceof Event)) {
148
- typeError(this, 'dispatchEvent', 'Event');
149
+ this.typeError('dispatchEvent', 'Event');
149
150
  } else if (!e.target) { // 初始化
150
151
  Object.defineProperty(e, 'target', {value: this, enumerable: true});
151
152
  e.stopPropagation = function() {
@@ -357,7 +358,7 @@ class AstElement extends AstNode {
357
358
  */
358
359
  matches(selector = '', simple = false) {
359
360
  if (typeof selector !== 'string') {
360
- typeError(this, 'matches', 'String');
361
+ this.typeError('matches', 'String');
361
362
  } else if (!selector.trim()) {
362
363
  return true;
363
364
  }
@@ -401,7 +402,8 @@ class AstElement extends AstNode {
401
402
  ),
402
403
  [type, ...parts] = plainSelector.trim().split('#'),
403
404
  name = parts.join('#');
404
- return (!type || this.type === type) && (!name || this.name === name)
405
+ return (!type || this.type === type || Boolean(Parser.typeAliases[this.type]?.includes(type)))
406
+ && (!name || this.name === name)
405
407
  && attributes.every(args => this.matchesAttr(...args));
406
408
  }
407
409
  /*
@@ -479,7 +481,7 @@ class AstElement extends AstNode {
479
481
  */
480
482
  comparePosition(other) {
481
483
  if (!(other instanceof AstElement)) {
482
- typeError(this, 'comparePosition', 'AstElement');
484
+ this.typeError('comparePosition', 'AstElement');
483
485
  } else if (this === other) {
484
486
  return 0;
485
487
  } else if (this.contains(other)) {
@@ -541,7 +543,7 @@ class AstElement extends AstNode {
541
543
  */
542
544
  posFromIndex(index) {
543
545
  if (typeof index !== 'number') {
544
- typeError(this, 'posFromIndex', 'Number');
546
+ this.typeError('posFromIndex', 'Number');
545
547
  }
546
548
  const text = this.toString();
547
549
  if (index < -text.length || index >= text.length || !Number.isInteger(index)) {
@@ -558,7 +560,7 @@ class AstElement extends AstNode {
558
560
  */
559
561
  indexFromPos(top, left) {
560
562
  if (typeof top !== 'number' || typeof left !== 'number') {
561
- typeError(this, 'indexFromPos', 'Number');
563
+ this.typeError('indexFromPos', 'Number');
562
564
  } else if (top < 0 || left < 0 || !Number.isInteger(top) || !Number.isInteger(left)) {
563
565
  return;
564
566
  }
@@ -582,7 +584,7 @@ class AstElement extends AstNode {
582
584
  */
583
585
  getRelativeIndex(j) {
584
586
  if (j !== undefined && typeof j !== 'number') {
585
- typeError(this, 'getRelativeIndex', 'Number');
587
+ this.typeError('getRelativeIndex', 'Number');
586
588
  }
587
589
  let /** @type {(string|this)[]} */ childNodes;
588
590
  /**
@@ -684,6 +686,63 @@ class AstElement extends AstNode {
684
686
  return node ? [[index + this.getRelativeIndex(i), node]] : [];
685
687
  });
686
688
  }
689
+
690
+ /**
691
+ * @template {'markup'|'json'} T
692
+ * @param {T} format
693
+ * @param {T extends 'markup' ? number : string} depth
694
+ * @returns {T extends 'markup' ? void : Record<string, any>}
695
+ */
696
+ print(format = 'markup', depth = 0) {
697
+ if (format === 'json') {
698
+ const {childNodes, ...prop} = this,
699
+ json = {
700
+ ...prop,
701
+ childNodes: childNodes.map(child => typeof child === 'string' ? child : child.print('json')),
702
+ };
703
+ if (typeof depth === 'string') {
704
+ fs.writeFileSync(
705
+ `${__dirname.slice(0, -3)}printed/${depth}${depth.endsWith('.json') ? '' : '.json'}`,
706
+ JSON.stringify(json, null, 2),
707
+ );
708
+ }
709
+ return json;
710
+ } else if (typeof depth !== 'number') {
711
+ this.typeError('print', 'Number');
712
+ }
713
+ const indent = ' '.repeat(depth),
714
+ str = this.toString(),
715
+ {childNodes, type, firstChild} = this,
716
+ {length} = childNodes;
717
+ if (!str || length === 0 || typeof firstChild === 'string' && firstChild === str) {
718
+ console.log(`${indent}\x1b[32m<%s>\x1b[0m${noWrap(str)}\x1b[32m</%s>\x1b[0m`, type, type);
719
+ return;
720
+ }
721
+ Parser.info(`${indent}<${type}>`);
722
+ let i = this.getPadding();
723
+ if (i) {
724
+ console.log(`${indent} ${noWrap(str.slice(0, i))}`);
725
+ }
726
+ for (const [j, child] of childNodes.entries()) {
727
+ const childStr = String(child),
728
+ gap = j === length - 1 ? 0 : this.getGaps(j);
729
+ if (!childStr) {
730
+ // pass
731
+ } else if (typeof child === 'string') {
732
+ console.log(`${indent} ${noWrap(child)}`);
733
+ } else {
734
+ child.print('markup', depth + 1);
735
+ }
736
+ i += childStr.length + gap;
737
+ if (gap) {
738
+ console.log(`${indent} ${noWrap(str.slice(i - gap, i))}`);
739
+ }
740
+ }
741
+ if (i < str.length) {
742
+ console.log(`${indent} ${noWrap(str.slice(i))}`);
743
+ }
744
+ Parser.info(`${indent}</${type}>`);
745
+ }
687
746
  }
688
747
 
689
748
  Parser.classes.AstElement = __filename;