wikiparser-node 0.1.0 → 0.2.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.
@@ -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
  }
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,13 +1,13 @@
1
1
  {
2
2
  "name": "wikiparser-node",
3
- "version": "0.1.0",
3
+ "version": "0.2.2",
4
4
  "description": "A Node.js parser for MediaWiki markup with AST",
5
5
  "keywords": [
6
6
  "mediawiki",
7
7
  "wikitext",
8
8
  "parser"
9
9
  ],
10
- "homepage": "https://github.com/bhsd-harry/wikiparser-node#readme",
10
+ "homepage": "https://github.com/bhsd-harry/wikiparser-node/wiki",
11
11
  "bugs": {
12
12
  "url": "https://github.com/bhsd-harry/wikiparser-node/issues"
13
13
  },
@@ -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/parser/table.js CHANGED
@@ -12,6 +12,7 @@ const parseTable = ({firstChild, type}, config = Parser.getConfig(), accum = [])
12
12
  TableToken = require('../src/table'),
13
13
  TrToken = require('../src/table/tr'),
14
14
  TdToken = require('../src/table/td'),
15
+ DdToken = require('../src/nowiki/dd'),
15
16
  /** @type {TrToken[]} */ stack = [],
16
17
  lines = firstChild.split('\n');
17
18
  let out = type === 'root' ? '' : `\n${lines.shift()}`;
@@ -33,10 +34,13 @@ const parseTable = ({firstChild, type}, config = Parser.getConfig(), accum = [])
33
34
  let top = stack.pop();
34
35
  const [spaces] = outLine.match(/^(?:\s|\x00\d+c\x7f)*/);
35
36
  const line = outLine.slice(spaces.length),
36
- matchesStart = line.match(/^(:*(?:\s|\x00\d+c\x7f)*)({\||{\x00\d+!\x7f|\x00\d+{\x7f)(.*)/);
37
+ matchesStart = line.match(/^(:*)((?:\s|\x00\d+c\x7f)*)({\||{\x00\d+!\x7f|\x00\d+{\x7f)(.*)/);
37
38
  if (matchesStart) {
38
- const [, indent, tableSyntax, attr] = matchesStart;
39
- push(`\n${spaces}${indent}\x00${accum.length}b\x7f`, top);
39
+ const [, indent, moreSpaces, tableSyntax, attr] = matchesStart;
40
+ if (indent) {
41
+ new DdToken(indent, config, accum);
42
+ }
43
+ push(`\n${spaces}${indent && `\x00${accum.length - 1}d\x7f`}${moreSpaces}\x00${accum.length}b\x7f`, top);
40
44
  const table = new TableToken(tableSyntax, attr, config, accum);
41
45
  stack.push(...top ? [top] : [], table);
42
46
  continue;
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
 
@@ -108,31 +108,24 @@ class AttributeToken extends Token {
108
108
  return super.getAttribute(key);
109
109
  }
110
110
 
111
- /** @complexity `n` */
112
- build() {
113
- super.build();
114
- if (this.type === 'ext-attr') {
115
- return this;
116
- }
117
- for (let [key, text] of this.#attr) {
118
- let built = false;
119
- if (key.includes('\x00')) {
120
- this.#attr.delete(key);
121
- key = this.buildFromStr(key).map(String).join('');
122
- built = true;
123
- }
124
- if (typeof text === 'string' && text.includes('\x00')) {
125
- text = this.buildFromStr(text).map(String).join('');
126
- built = true;
127
- }
128
- if (built) {
129
- this.#attr.set(key, text);
111
+ afterBuild() {
112
+ if (this.type !== 'ext-attr') {
113
+ for (let [key, text] of this.#attr) {
114
+ let built = false;
115
+ if (key.includes('\x00')) {
116
+ this.#attr.delete(key);
117
+ key = this.buildFromStr(key).map(String).join('');
118
+ built = true;
119
+ }
120
+ if (typeof text === 'string' && text.includes('\x00')) {
121
+ text = this.buildFromStr(text).map(String).join('');
122
+ built = true;
123
+ }
124
+ if (built) {
125
+ this.#attr.set(key, text);
126
+ }
130
127
  }
131
128
  }
132
- return this;
133
- }
134
-
135
- afterBuild() {
136
129
  const that = this,
137
130
  /** @type {AstListener} */ attributeListener = ({type, target}) => {
138
131
  if (type === 'text' || target !== that) {
@@ -241,13 +234,13 @@ class AttributeToken extends Token {
241
234
  }
242
235
 
243
236
  #leadingSpace(str = super.toString()) {
244
- return str && !/^\s/.test(str) ? ' ' : '';
237
+ return this.type !== 'table-attr' && str && !/^\s/.test(str) ? ' ' : '';
245
238
  }
246
239
 
240
+ /** @this {AttributeToken & Token} */
247
241
  toString() {
248
- let str = super.toString();
249
- str = `${this.#leadingSpace(str)}${str}`;
250
- return this.type === 'table-attr' ? str.replaceAll('\n', ' ') : str;
242
+ const str = this.type === 'table-attr' ? normalizeSpace(this) : super.toString();
243
+ return `${this.#leadingSpace(str)}${str}`;
251
244
  }
252
245
 
253
246
  getPadding() {
@@ -257,7 +250,7 @@ class AttributeToken extends Token {
257
250
  text() {
258
251
  let str = this.#updateFromAttr();
259
252
  str = `${this.#leadingSpace(str)}${str}`;
260
- return this.type === 'table-attr' ? str.replaceAll('\n', ' ') : str;
253
+ return this.type === 'table-attr' ? normalizeSpace(str) : str;
261
254
  }
262
255
 
263
256
  /** @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;