wikiparser-node 0.1.0 → 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.
package/README.md CHANGED
@@ -181,12 +181,35 @@
181
181
  2. [setTarget](#magiclinktoken.settarget)
182
182
  2. [原型属性](#magiclinktoken.prototype.properties)
183
183
  1. [protocol](#magiclinktoken.protocol)
184
- 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. [选择器](#选择器)
185
208
  1. [type](#selector.type)
186
209
  2. [name](#selector.name)
187
210
  3. [属性](#selector.attribute)
188
211
  4. [伪选择器](#selector.pseudo)
189
- 22. [$ (TokenCollection)](#-tokencollection)
212
+ 24. [$ (TokenCollection)](#-tokencollection)
190
213
  </details>
191
214
 
192
215
  # Parser
@@ -1807,6 +1830,192 @@ assert(root.toString() === 'https://example.org');
1807
1830
 
1808
1831
  [返回目录](#目录)
1809
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
+
1810
2019
  # 选择器
1811
2020
  Token 选择器的设计仿照了 CSS 和 jQuery 的选择器。
1812
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/index.js CHANGED
@@ -77,6 +77,7 @@ const /** @type {Parser} */ Parser = {
77
77
  ['ExtLinkToken'],
78
78
  ['MagicLinkToken'],
79
79
  ['ListToken', 'DdToken'],
80
+ ['ConverterToken'],
80
81
  ],
81
82
 
82
83
  config: './config/default',
@@ -178,6 +179,47 @@ const /** @type {Parser} */ Parser = {
178
179
  delete require.cache[require.resolve('./tool')];
179
180
  return require('./tool');
180
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
+ },
181
223
  };
182
224
 
183
225
  const /** @type {PropertyDescriptorMap} */ def = {};
package/lib/element.js CHANGED
@@ -402,7 +402,8 @@ class AstElement extends AstNode {
402
402
  ),
403
403
  [type, ...parts] = plainSelector.trim().split('#'),
404
404
  name = parts.join('#');
405
- 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)
406
407
  && attributes.every(args => this.matchesAttr(...args));
407
408
  }
408
409
  /*
package/mixin/sol.js CHANGED
@@ -11,14 +11,18 @@ const /** @type {Parser} */ Parser = require('..'),
11
11
  const sol = constructor => class extends constructor {
12
12
  /** @this {Token} */
13
13
  prependNewLine() {
14
- const {previousVisibleSibling} = this;
15
- return previousVisibleSibling && !String(previousVisibleSibling).endsWith('\n') ? '\n' : '';
14
+ const {previousVisibleSibling = '', parentNode} = this;
15
+ return (previousVisibleSibling || parentNode?.type !== 'root') && !String(previousVisibleSibling).endsWith('\n')
16
+ ? '\n'
17
+ : '';
16
18
  }
17
19
 
18
20
  /** @this {Token} */
19
21
  appendNewLine() {
20
- const {nextVisibleSibling} = this;
21
- return nextVisibleSibling && !String(nextVisibleSibling).startsWith('\n') ? '\n' : '';
22
+ const {nextVisibleSibling = '', parentNode} = this;
23
+ return (nextVisibleSibling || parentNode?.type !== 'root') && !String(nextVisibleSibling ?? '').startsWith('\n')
24
+ ? '\n'
25
+ : '';
22
26
  }
23
27
 
24
28
  toString(ownLine = false) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikiparser-node",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "A Node.js parser for MediaWiki markup with AST",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ const /** @type {Parser} */ Parser = require('..');
4
+
5
+ /**
6
+ * @param {string} firstChild
7
+ * @param {accum} accum
8
+ */
9
+ const parseConverter = (firstChild, config = Parser.getConfig(), accum = []) => {
10
+ const ConverterToken = require('../src/converter'),
11
+ regex1 = /-{/g,
12
+ regex2 = /-{|}-/g,
13
+ /** @type {RegExpExecArray[]} */ stack = [];
14
+ let regex = regex1,
15
+ mt = regex.exec(firstChild);
16
+ while (mt) {
17
+ const {0: syntax, index} = mt;
18
+ if (syntax === '}-') {
19
+ const top = stack.pop(),
20
+ {length} = accum,
21
+ str = firstChild.slice(top.index + 2, index),
22
+ i = str.indexOf('|'),
23
+ [flags, text] = i === -1 ? [[], str] : [str.slice(0, i).split(';'), str.slice(i + 1)],
24
+ temp = text.replace(/(?<=&[#a-z0-9]+);/i, '\x01'),
25
+ variants = `(?:${config.variants.join('|')})`,
26
+ rules = temp.split(new RegExp(`;(?=\\s*(?:${variants}|[^;]*?=>\\s*${variants})\\s*:)`))
27
+ .map(rule => rule.replaceAll('\x01', ';'));
28
+ new ConverterToken(flags, rules, config, accum);
29
+ firstChild = `${firstChild.slice(0, top.index)}\x00${length}v\x7f${firstChild.slice(index + 2)}`;
30
+ if (stack.length === 0) {
31
+ regex = regex1;
32
+ }
33
+ regex.lastIndex = top.index + 3 + String(length).length;
34
+ } else {
35
+ stack.push(mt);
36
+ regex = regex2;
37
+ }
38
+ mt = regex.exec(firstChild);
39
+ }
40
+ return firstChild;
41
+ };
42
+
43
+ Parser.parsers.parseConverter = __filename;
44
+ module.exports = parseConverter;
package/src/arg.js CHANGED
@@ -75,13 +75,13 @@ class ArgToken extends Token {
75
75
 
76
76
  /** @returns {[number, string][]} */
77
77
  plain() {
78
- return this.childElementCount > 1 ? this.children[1].plain() : [];
78
+ return this.childNodes.length > 1 ? this.children[1].plain() : [];
79
79
  }
80
80
 
81
81
  /** @complexity `n` */
82
82
  removeRedundant() {
83
83
  Parser.run(() => {
84
- for (let i = this.childElementCount - 1; i > 1; i--) {
84
+ for (let i = this.childNodes.length - 1; i > 1; i--) {
85
85
  super.removeAt(i);
86
86
  }
87
87
  });
@@ -100,8 +100,8 @@ class ArgToken extends Token {
100
100
  }
101
101
 
102
102
  /** @param {Token} token */
103
- insertAt(token, i = this.childElementCount) {
104
- const j = i < 0 ? i + this.childElementCount : i;
103
+ insertAt(token, i = this.childNodes.length) {
104
+ const j = i < 0 ? i + this.childNodes.length : i;
105
105
  if (j > 1 && !Parser.running) {
106
106
  throw new RangeError(`${this.constructor.name} 不可插入 arg-redundant 子节点!`);
107
107
  }
@@ -117,7 +117,7 @@ class ArgToken extends Token {
117
117
  name = String(name);
118
118
  const root = Parser.parse(`{{{${name}}}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
119
119
  {childNodes: {length}, firstElementChild} = root;
120
- if (length !== 1 || firstElementChild?.type !== 'arg' || firstElementChild.childElementCount !== 1) {
120
+ if (length !== 1 || firstElementChild?.type !== 'arg' || firstElementChild.childNodes.length !== 1) {
121
121
  throw new SyntaxError(`非法的参数名称:${noWrap(name)}`);
122
122
  }
123
123
  const newName = firstElementChild.firstElementChild;
@@ -131,7 +131,7 @@ class ArgToken extends Token {
131
131
  value = String(value);
132
132
  const root = Parser.parse(`{{{|${value}}}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
133
133
  {childNodes: {length}, firstElementChild} = root;
134
- if (length !== 1 || firstElementChild?.type !== 'arg' || firstElementChild.childElementCount !== 2) {
134
+ if (length !== 1 || firstElementChild?.type !== 'arg' || firstElementChild.childNodes.length !== 2) {
135
135
  throw new SyntaxError(`非法的参数预设值:${noWrap(value)}`);
136
136
  }
137
137
  const [, oldDefault] = this.children,
package/src/attribute.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const {externalUse} = require('../util/debug'),
4
- {toCase, removeComment} = require('../util/string'),
4
+ {toCase, removeComment, normalizeSpace} = require('../util/string'),
5
5
  /** @type {Parser} */ Parser = require('..'),
6
6
  Token = require('.');
7
7
 
@@ -241,13 +241,13 @@ class AttributeToken extends Token {
241
241
  }
242
242
 
243
243
  #leadingSpace(str = super.toString()) {
244
- return str && !/^\s/.test(str) ? ' ' : '';
244
+ return this.type !== 'table-attr' && str && !/^\s/.test(str) ? ' ' : '';
245
245
  }
246
246
 
247
+ /** @this {AttributeToken & Token} */
247
248
  toString() {
248
- let str = super.toString();
249
- str = `${this.#leadingSpace(str)}${str}`;
250
- return this.type === 'table-attr' ? str.replaceAll('\n', ' ') : str;
249
+ const str = this.type === 'table-attr' ? normalizeSpace(this) : super.toString();
250
+ return `${this.#leadingSpace(str)}${str}`;
251
251
  }
252
252
 
253
253
  getPadding() {
@@ -257,7 +257,7 @@ class AttributeToken extends Token {
257
257
  text() {
258
258
  let str = this.#updateFromAttr();
259
259
  str = `${this.#leadingSpace(str)}${str}`;
260
- return this.type === 'table-attr' ? str.replaceAll('\n', ' ') : str;
260
+ return this.type === 'table-attr' ? normalizeSpace(str) : str;
261
261
  }
262
262
 
263
263
  /** @returns {[number, string][]} */
@@ -0,0 +1,135 @@
1
+ 'use strict';
2
+
3
+ const {text} = require('../util/string'),
4
+ /** @type {Parser} */ Parser = require('..'),
5
+ Token = require('.'),
6
+ ConverterFlagsToken = require('./converterFlags'),
7
+ ConverterRuleToken = require('./converterRule');
8
+
9
+ /**
10
+ * 转换
11
+ * @classdesc `{childNodes: [ConverterFlagsToken, ...ConverterRuleToken]}`
12
+ */
13
+ class ConverterToken extends Token {
14
+ type = 'converter';
15
+
16
+ /**
17
+ * @param {string[]} flags
18
+ * @param {string[]} rules
19
+ * @param {accum} accum
20
+ */
21
+ constructor(flags, rules, config = Parser.getConfig(), accum = []) {
22
+ super(undefined, config, false, accum);
23
+ this.append(new ConverterFlagsToken(flags, config, accum));
24
+ if (rules.length) {
25
+ const [firstRule] = rules,
26
+ hasColon = firstRule.includes(':'),
27
+ firstRuleToken = new ConverterRuleToken(firstRule, hasColon, config, accum);
28
+ if (hasColon && firstRuleToken.childNodes.length === 1) {
29
+ this.appendChild(new ConverterRuleToken(rules.join(';'), false, config, accum));
30
+ } else {
31
+ this.append(
32
+ firstRuleToken,
33
+ ...rules.slice(1).map(rule => new ConverterRuleToken(rule, true, config, accum)),
34
+ );
35
+ }
36
+ }
37
+ this.protectChildren(0);
38
+ }
39
+
40
+ cloneNode() {
41
+ const [flags, ...rules] = this.cloneChildren(),
42
+ token = Parser.run(() => new ConverterToken([], [], this.getAttribute('config')));
43
+ token.firstElementChild.safeReplaceWith(flags);
44
+ token.append(...rules);
45
+ return token;
46
+ }
47
+
48
+ toString() {
49
+ const [flags, ...rules] = this.children;
50
+ return `-{${flags.toString()}${flags.childNodes.length ? '|' : ''}${rules.map(String).join(';')}}-`;
51
+ }
52
+
53
+ getPadding() {
54
+ return 2;
55
+ }
56
+
57
+ /** @param {number} i */
58
+ getGaps(i = 0) {
59
+ i = i < 0 ? i + this.childNodes.length : i;
60
+ return i || this.firstElementChild.childNodes.length ? 1 : 0;
61
+ }
62
+
63
+ text() {
64
+ const [flags, ...rules] = this.children;
65
+ return `-{${flags.text()}|${text(rules, ';')}}-`;
66
+ }
67
+
68
+ /** @returns {[number, string][]} */
69
+ plain() {
70
+ return this.children.slice(1).flatMap(child => child.plain());
71
+ }
72
+
73
+ /** @this {ConverterToken & {firstChild: ConverterFlagsToken}} */
74
+ getAllFlags() {
75
+ return this.firstChild.getAllFlags();
76
+ }
77
+
78
+ /** @this {ConverterToken & {firstChild: ConverterFlagsToken}} */
79
+ getEffectiveFlags() {
80
+ return this.firstChild.getEffectiveFlags();
81
+ }
82
+
83
+ /** @this {ConverterToken & {firstChild: ConverterFlagsToken}} */
84
+ getUnknownFlags() {
85
+ return this.firstChild.getUnknownFlags();
86
+ }
87
+
88
+ /**
89
+ * @this {ConverterToken & {firstChild: ConverterFlagsToken}}
90
+ * @param {string} flag
91
+ */
92
+ hasFlag(flag) {
93
+ return this.firstChild.hasFlag(flag);
94
+ }
95
+
96
+ /**
97
+ * @this {ConverterToken & {firstChild: ConverterFlagsToken}}
98
+ * @param {string} flag
99
+ */
100
+ hasEffectiveFlag(flag) {
101
+ return this.firstChild.hasEffectiveFlag(flag);
102
+ }
103
+
104
+ /**
105
+ * @this {ConverterToken & {firstChild: ConverterFlagsToken}}
106
+ * @param {string} flag
107
+ */
108
+ removeFlag(flag) {
109
+ this.firstChild.removeFlag(flag);
110
+ }
111
+
112
+ /**
113
+ * @this {ConverterToken & {firstChild: ConverterFlagsToken}}
114
+ * @param {string} flag
115
+ */
116
+ setFlag(flag) {
117
+ this.firstChild.setFlag(flag);
118
+ }
119
+
120
+ /**
121
+ * @this {ConverterToken & {firstChild: ConverterFlagsToken}}
122
+ * @param {string} flag
123
+ */
124
+ toggleFlag(flag) {
125
+ this.firstChild.toggleFlag(flag);
126
+ }
127
+
128
+ /** @this {ConverterToken & {children: [ConverterFlagsToken, ConverterRuleToken]}} */
129
+ get noConvert() {
130
+ return this.childNodes.length < 3 && !this.children[1]?.variant;
131
+ }
132
+ }
133
+
134
+ Parser.classes.ConverterToken = __filename;
135
+ module.exports = ConverterToken;