wikiparser-node 0.0.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 (65) hide show
  1. package/.eslintrc.json +229 -0
  2. package/LICENSE +674 -0
  3. package/README.md +1896 -0
  4. package/config/default.json +766 -0
  5. package/config/llwiki.json +686 -0
  6. package/config/moegirl.json +721 -0
  7. package/index.js +159 -0
  8. package/jsconfig.json +7 -0
  9. package/lib/element.js +690 -0
  10. package/lib/node.js +357 -0
  11. package/lib/ranges.js +122 -0
  12. package/lib/title.js +57 -0
  13. package/mixin/attributeParent.js +67 -0
  14. package/mixin/fixedToken.js +32 -0
  15. package/mixin/hidden.js +22 -0
  16. package/package.json +30 -0
  17. package/parser/brackets.js +107 -0
  18. package/parser/commentAndExt.js +61 -0
  19. package/parser/externalLinks.js +30 -0
  20. package/parser/hrAndDoubleUnderscore.js +26 -0
  21. package/parser/html.js +41 -0
  22. package/parser/links.js +92 -0
  23. package/parser/magicLinks.js +40 -0
  24. package/parser/quotes.js +63 -0
  25. package/parser/table.js +97 -0
  26. package/src/arg.js +150 -0
  27. package/src/atom/hidden.js +10 -0
  28. package/src/atom/index.js +33 -0
  29. package/src/attribute.js +342 -0
  30. package/src/extLink.js +116 -0
  31. package/src/heading.js +91 -0
  32. package/src/html.js +144 -0
  33. package/src/imageParameter.js +172 -0
  34. package/src/index.js +602 -0
  35. package/src/link/category.js +88 -0
  36. package/src/link/file.js +201 -0
  37. package/src/link/index.js +214 -0
  38. package/src/listToken.js +47 -0
  39. package/src/magicLink.js +66 -0
  40. package/src/nowiki/comment.js +45 -0
  41. package/src/nowiki/doubleUnderscore.js +42 -0
  42. package/src/nowiki/hr.js +41 -0
  43. package/src/nowiki/index.js +37 -0
  44. package/src/nowiki/noinclude.js +24 -0
  45. package/src/nowiki/quote.js +37 -0
  46. package/src/onlyinclude.js +42 -0
  47. package/src/parameter.js +165 -0
  48. package/src/syntax.js +80 -0
  49. package/src/table/index.js +867 -0
  50. package/src/table/td.js +259 -0
  51. package/src/table/tr.js +244 -0
  52. package/src/tagPair/ext.js +85 -0
  53. package/src/tagPair/include.js +45 -0
  54. package/src/tagPair/index.js +91 -0
  55. package/src/transclude.js +627 -0
  56. package/tool/index.js +898 -0
  57. package/typings/element.d.ts +28 -0
  58. package/typings/index.d.ts +49 -0
  59. package/typings/node.d.ts +23 -0
  60. package/typings/parser.d.ts +9 -0
  61. package/typings/table.d.ts +14 -0
  62. package/typings/token.d.ts +21 -0
  63. package/typings/tool.d.ts +10 -0
  64. package/util/debug.js +70 -0
  65. package/util/string.js +60 -0
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ const hidden = require('../../mixin/hidden'),
4
+ /** @type {Parser} */ Parser = require('../..'),
5
+ NowikiToken = require('.');
6
+
7
+ /**
8
+ * `<noinclude>`和`</noinclude>`,不可进行任何更改
9
+ * @classdesc `{childNodes: [string]}`
10
+ */
11
+ class NoincludeToken extends hidden(NowikiToken) {
12
+ type = 'noinclude';
13
+
14
+ /** @param {string} str */
15
+ setText(str) {
16
+ if (/^<\/?(?:(?:no|only)include|includeonly)(?:\s.*)?\/?>$/is.test(this.toString())) {
17
+ throw new Error(`${this.constructor.name} 不可更改文字内容!`);
18
+ }
19
+ return super.setText(str);
20
+ }
21
+ }
22
+
23
+ Parser.classes.NoincludeToken = __filename;
24
+ module.exports = NoincludeToken;
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ const /** @type {Parser} */ Parser = require('../..'),
4
+ NowikiToken = require('.');
5
+
6
+ /**
7
+ * `<hr>`
8
+ * @classdesc `{childNodes: [string]}`
9
+ */
10
+ class QuoteToken extends NowikiToken {
11
+ type = 'quote';
12
+
13
+ /**
14
+ * @param {number} n
15
+ * @param {accum} accum
16
+ */
17
+ constructor(n, config = Parser.getConfig(), accum = []) {
18
+ super("'".repeat(n), config, accum);
19
+ this.setAttribute('name', String(n));
20
+ }
21
+
22
+ /** @param {string} str */
23
+ setText(str) {
24
+ if (!["''", "'''", "'''''"].includes(str)) {
25
+ throw new RangeError(`${this.constructor.name} 的内部文本只能为连续 2/3/5 个"'"!`);
26
+ }
27
+ return super.setText(str);
28
+ }
29
+
30
+ /** @returns {[number, string][]} */
31
+ plain() {
32
+ return [];
33
+ }
34
+ }
35
+
36
+ Parser.classes.QuoteToken = __filename;
37
+ module.exports = QuoteToken;
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ const /** @type {Parser} */ Parser = require('..'),
4
+ Token = require('.');
5
+
6
+ /**
7
+ * 嵌入时的`<onlyinclude>`
8
+ * @classdesc `{childNodes: ...string|Token}`
9
+ */
10
+ class OnlyincludeToken extends Token {
11
+ type = 'onlyinclude';
12
+
13
+ /**
14
+ * @param {string} inner
15
+ * @param {accum} accum
16
+ */
17
+ constructor(inner, config = Parser.getConfig(), accum = []) {
18
+ super(inner, config, false, accum);
19
+ }
20
+
21
+ cloneNode() {
22
+ const cloned = this.cloneChildren(),
23
+ token = Parser.run(() => new OnlyincludeToken(undefined, this.getAttribute('config')));
24
+ token.append(...cloned);
25
+ return token;
26
+ }
27
+
28
+ toString() {
29
+ return `<onlyinclude>${super.toString()}</onlyinclude>`;
30
+ }
31
+
32
+ getPadding() {
33
+ return 13;
34
+ }
35
+
36
+ isPlain() {
37
+ return this.constructor === OnlyincludeToken;
38
+ }
39
+ }
40
+
41
+ Parser.classes.OnlyincludeToken = __filename;
42
+ module.exports = OnlyincludeToken;
@@ -0,0 +1,165 @@
1
+ 'use strict';
2
+
3
+ const {typeError} = require('../util/debug'),
4
+ fixedToken = require('../mixin/fixedToken'),
5
+ /** @type {Parser} */ Parser = require('..'),
6
+ Token = require('.');
7
+
8
+ /**
9
+ * 模板或魔术字参数
10
+ * @classdesc `{childNodes: [AtomToken, Token]}`
11
+ */
12
+ class ParameterToken extends fixedToken(Token) {
13
+ type = 'parameter';
14
+ anon;
15
+
16
+ /**
17
+ * @param {string|number} key
18
+ * @param {string} value
19
+ * @param {accum} accum
20
+ */
21
+ constructor(key, value, config = Parser.getConfig(), accum = []) {
22
+ super(undefined, config, true, accum);
23
+ this.anon = typeof key === 'number';
24
+ const AtomToken = require('./atom'),
25
+ keyToken = new AtomToken(this.anon ? undefined : key, 'parameter-key', config, accum, {
26
+ 'Stage-2': ':', '!HeadingToken': '',
27
+ }),
28
+ token = new Token(value, config, true, accum);
29
+ token.type = 'parameter-value';
30
+ this.append(keyToken, token.setAttribute('stage', 2));
31
+ }
32
+
33
+ cloneNode() {
34
+ const [key, value] = this.cloneChildren(),
35
+ config = this.getAttribute('config');
36
+ return Parser.run(() => {
37
+ const token = new ParameterToken(this.anon ? Number(this.name) : undefined, undefined, config);
38
+ token.firstElementChild.safeReplaceWith(key);
39
+ token.lastElementChild.safeReplaceWith(value);
40
+ return token.afterBuild();
41
+ });
42
+ }
43
+
44
+ afterBuild() {
45
+ if (!this.anon) {
46
+ const name = this.firstElementChild.text().trim(),
47
+ {parentNode} = this;
48
+ this.setAttribute('name', name);
49
+ if (parentNode && parentNode instanceof require('./transclude')) {
50
+ parentNode.getAttribute('keys').add(name);
51
+ parentNode.getArgs(name, false, false).add(this);
52
+ }
53
+ }
54
+ const that = this;
55
+ /**
56
+ * 在AstEventData中记录`oldKey`和`newKey`
57
+ * @type {AstListener}
58
+ */
59
+ const parameterListener = ({prevTarget}, data) => {
60
+ if (!that.anon) { // 匿名参数不管怎么变动还是匿名
61
+ const {firstElementChild} = that;
62
+ if (prevTarget === firstElementChild) {
63
+ const newKey = firstElementChild.text().trim();
64
+ data.oldKey = that.name;
65
+ data.newKey = newKey;
66
+ that.setAttribute('name', newKey);
67
+ }
68
+ }
69
+ };
70
+ this.addEventListener(['remove', 'insert', 'replace', 'text'], parameterListener);
71
+ return this;
72
+ }
73
+
74
+ /** @returns {string} */
75
+ toString() {
76
+ return this.anon ? this.lastElementChild.toString() : super.toString('=');
77
+ }
78
+
79
+ getGaps() {
80
+ return this.anon ? 0 : 1;
81
+ }
82
+
83
+ /** @returns {string} */
84
+ text() {
85
+ return this.anon ? this.lastElementChild.text() : super.text('=');
86
+ }
87
+
88
+ /** @returns {[number, string][]} */
89
+ plain() {
90
+ return this.lastElementChild.plain();
91
+ }
92
+
93
+ /**
94
+ * @param {ParameterToken} token
95
+ * @complexity `n`
96
+ */
97
+ safeReplaceWith(token) {
98
+ Parser.warn(`${this.constructor.name}.safeReplaceWith 方法退化到 replaceWith。`);
99
+ return this.replaceWith(token);
100
+ }
101
+
102
+ getValue() {
103
+ const value = this.lastElementChild.text();
104
+ return this.anon && this.parentNode?.matches('template, magic-word#invoke') ? value : value.trim();
105
+ }
106
+
107
+ /** @param {string} value */
108
+ setValue(value) {
109
+ value = String(value);
110
+ const templateLike = this.parentElement?.matches('template, magic-word#invoke'),
111
+ wikitext = `{{${templateLike ? ':T|' : 'lc:'}${this.anon ? '' : '1='}${value}}}`,
112
+ root = Parser.parse(wikitext, this.getAttribute('include'), 2, this.getAttribute('config')),
113
+ {childNodes: {length}, firstElementChild} = root,
114
+ /** @type {ParameterToken} */ lastElementChild = firstElementChild?.lastElementChild;
115
+ if (length !== 1 || !firstElementChild?.matches(templateLike ? 'template#T' : 'magic-word#lc')
116
+ || firstElementChild.childElementCount !== 2
117
+ || lastElementChild.anon !== this.anon || lastElementChild.name !== '1'
118
+ ) {
119
+ throw new SyntaxError(`非法的模板参数:${value.replaceAll('\n', '\\n')}`);
120
+ }
121
+ const newValue = lastElementChild.lastChild;
122
+ root.destroy();
123
+ firstElementChild.destroy();
124
+ lastElementChild.destroy();
125
+ this.lastElementChild.safeReplaceWith(newValue);
126
+ }
127
+
128
+ /** @param {string} key */
129
+ rename(key, force = false) {
130
+ if (typeof key !== 'string') {
131
+ typeError(this, 'rename', 'String');
132
+ }
133
+ const {parentNode} = this;
134
+ // 必须检测是否是TranscludeToken
135
+ if (!parentNode || !parentNode.matches('template, magic-word#invoke')
136
+ || !(parentNode instanceof require('./transclude'))
137
+ ) {
138
+ throw new Error(`${this.constructor.name}.rename 方法仅用于模板参数!`);
139
+ }
140
+ const root = Parser.parse(`{{:T|${key}=}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
141
+ {childNodes: {length}, firstElementChild} = root;
142
+ if (length !== 1 || !firstElementChild?.matches('template#T') || firstElementChild.childElementCount !== 2) {
143
+ throw new SyntaxError(`非法的模板参数名:${key}`);
144
+ }
145
+ const {lastElementChild} = firstElementChild,
146
+ {name} = lastElementChild,
147
+ keyToken = lastElementChild.firstChild;
148
+ if (this.name === name) {
149
+ Parser.warn('未改变实际参数名', name);
150
+ } else if (parentNode.hasArg(name)) {
151
+ if (force) {
152
+ Parser.warn('参数更名造成重复参数', name);
153
+ } else {
154
+ throw new RangeError(`参数更名造成重复参数:${name}`);
155
+ }
156
+ }
157
+ root.destroy();
158
+ firstElementChild.destroy();
159
+ lastElementChild.destroy();
160
+ this.firstElementChild.safeReplaceWith(keyToken);
161
+ }
162
+ }
163
+
164
+ Parser.classes.ParameterToken = __filename;
165
+ module.exports = ParameterToken;
package/src/syntax.js ADDED
@@ -0,0 +1,80 @@
1
+ 'use strict';
2
+
3
+ const {undo} = require('../util/debug'),
4
+ {text} = require('../util/string'),
5
+ /** @type {Parser} */ Parser = require('..'),
6
+ Token = require('.');
7
+
8
+ /**
9
+ * 满足特定语法格式的plain Token
10
+ * @classdesc `{childNodes: (string|Token)[]}`
11
+ */
12
+ class SyntaxToken extends Token {
13
+ #pattern;
14
+
15
+ /**
16
+ * @param {?string} wikitext
17
+ * @param {RegExp} pattern
18
+ * @param {accum} accum
19
+ * @param {acceptable} acceptable
20
+ */
21
+ constructor(wikitext, pattern, type = 'plain', config = Parser.getConfig(), accum = [], acceptable = null) {
22
+ super(wikitext, config, true, accum, acceptable);
23
+ this.type = type;
24
+ this.#pattern = pattern;
25
+ }
26
+
27
+ cloneNode() {
28
+ const cloned = this.cloneChildren(),
29
+ config = this.getAttribute('config'),
30
+ acceptable = this.getAttribute('acceptable');
31
+ return Parser.run(() => {
32
+ const token = new SyntaxToken(undefined, this.#pattern, this.type, config, [], acceptable);
33
+ token.append(...cloned);
34
+ return token.afterBuild();
35
+ });
36
+ }
37
+
38
+ afterBuild() {
39
+ const that = this,
40
+ /** @type {AstListener} */ syntaxListener = (e, data) => {
41
+ if (!Parser.running && !that.#pattern.test(that.text())) {
42
+ undo(e, data);
43
+ }
44
+ };
45
+ this.addEventListener(['remove', 'insert', 'replace', 'text'], syntaxListener);
46
+ return this;
47
+ }
48
+
49
+ /**
50
+ * @template {string} T
51
+ * @param {T} key
52
+ * @returns {TokenAttribute<T>}
53
+ */
54
+ getAttribute(key) {
55
+ if (key === 'pattern') {
56
+ return this.#pattern;
57
+ }
58
+ return super.getAttribute(key);
59
+ }
60
+
61
+ /** @returns {[number, string][]} */
62
+ plain() {
63
+ return [];
64
+ }
65
+
66
+ /**
67
+ * @param {...string|Token} elements
68
+ * @complexity `n`
69
+ */
70
+ replaceChildren(...elements) {
71
+ if (this.#pattern.test(text(elements))) {
72
+ Parser.run(() => {
73
+ super.replaceChildren(...elements);
74
+ });
75
+ }
76
+ }
77
+ }
78
+
79
+ Parser.classes.SyntaxToken = __filename;
80
+ module.exports = SyntaxToken;