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 +211 -2
- package/config/default.json +12 -1
- package/config/llwiki.json +12 -1
- package/config/moegirl.json +9 -1
- package/errors/2022-07-04T22:30:41.785Z +1 -0
- package/errors/2022-07-04T22:30:41.785Z.err +11 -0
- package/errors/2022-07-04T22:30:41.785Z.json +5 -0
- package/index.js +42 -0
- package/lib/element.js +2 -1
- package/mixin/sol.js +8 -4
- package/package.json +1 -1
- package/parser/converter.js +44 -0
- package/src/arg.js +6 -6
- package/src/attribute.js +6 -6
- package/src/converter.js +135 -0
- package/src/converterFlags.js +214 -0
- package/src/converterRule.js +209 -0
- package/src/extLink.js +11 -11
- package/src/imageParameter.js +1 -1
- package/src/index.js +14 -10
- package/src/link/file.js +2 -2
- package/src/link/index.js +7 -7
- package/src/parameter.js +2 -2
- package/src/syntax.js +3 -1
- package/src/table/td.js +1 -0
- package/src/table/tr.js +1 -1
- package/src/transclude.js +14 -14
- package/typings/index.d.ts +2 -0
- package/typings/node.d.ts +2 -2
- package/typings/token.d.ts +1 -0
- package/util/string.js +13 -1
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const /** @type {Parser} */ Parser = require('..'),
|
|
4
|
+
Token = require('.'),
|
|
5
|
+
AtomToken = require('./atom');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 转换flags
|
|
9
|
+
* @classdesc `{childNodes: ...AtomToken}`
|
|
10
|
+
*/
|
|
11
|
+
class ConverterFlagsToken extends Token {
|
|
12
|
+
type = 'converter-flags';
|
|
13
|
+
/** @type {string[]} */ #flags;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {string[]} flags
|
|
17
|
+
* @param {accum} accum
|
|
18
|
+
*/
|
|
19
|
+
constructor(flags, config = Parser.getConfig(), accum = []) {
|
|
20
|
+
super(undefined, config, false, accum, {AtomToken: ':'});
|
|
21
|
+
this.append(...flags.map(flag => new AtomToken(flag, 'converter-flag', config, accum)));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
cloneNode() {
|
|
25
|
+
const cloned = this.cloneChildren(),
|
|
26
|
+
token = Parser.run(() => new ConverterFlagsToken([], this.getAttribute('config')));
|
|
27
|
+
token.append(...cloned);
|
|
28
|
+
token.afterBuild();
|
|
29
|
+
return token;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** @complexity `n` */
|
|
33
|
+
afterBuild() {
|
|
34
|
+
this.#flags = this.children.map(child => child.text().trim());
|
|
35
|
+
const that = this,
|
|
36
|
+
/** @type {AstListener} */ converterFlagsListener = ({prevTarget}) => {
|
|
37
|
+
if (prevTarget) {
|
|
38
|
+
that.#flags[that.childNodes.indexOf(prevTarget)] = prevTarget.text().trim();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
this.addEventListener(['remove', 'insert', 'text', 'replace'], converterFlagsListener);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @template {string} T
|
|
46
|
+
* @param {T} key
|
|
47
|
+
* @returns {TokenAttribute<T>}
|
|
48
|
+
*/
|
|
49
|
+
getAttribute(key) {
|
|
50
|
+
if (key === 'flags') {
|
|
51
|
+
return Parser.debugging ? this.#flags : [...this.#flags];
|
|
52
|
+
}
|
|
53
|
+
return super.getAttribute(key);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {number} i
|
|
58
|
+
* @complexity `n`
|
|
59
|
+
*/
|
|
60
|
+
removeAt(i) {
|
|
61
|
+
const /** @type {AtomToken} */ token = super.removeAt(i);
|
|
62
|
+
this.#flags?.splice(i, 1);
|
|
63
|
+
return token;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {AtomToken} token
|
|
68
|
+
* @complexity `n`
|
|
69
|
+
*/
|
|
70
|
+
insertAt(token, i = this.childNodes.length) {
|
|
71
|
+
super.insertAt(token, i);
|
|
72
|
+
this.#flags?.splice(i, 0, token.text());
|
|
73
|
+
return token;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
toString() {
|
|
77
|
+
return super.toString(';');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getGaps() {
|
|
81
|
+
return 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
text() {
|
|
85
|
+
return super.text(';');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** @returns {[number, string][]} */
|
|
89
|
+
plain() {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @param {string} flag
|
|
95
|
+
* @returns {AtomToken[]}
|
|
96
|
+
* @complexity `n`
|
|
97
|
+
*/
|
|
98
|
+
getFlagToken(flag) {
|
|
99
|
+
return this.#flags.includes(flag) ? this.children.filter(child => child.text().trim() === flag) : [];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getAllFlags() {
|
|
103
|
+
return new Set(this.#flags);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** @complexity `n` */
|
|
107
|
+
getEffectiveFlags() {
|
|
108
|
+
const {variants} = this.getAttribute('config'),
|
|
109
|
+
variantFlags = this.#flags.filter(flag => variants.includes(flag)),
|
|
110
|
+
unknownFlags = this.#flags.filter(flag => /{{.+}}/.test(flag));
|
|
111
|
+
if (variantFlags.length) {
|
|
112
|
+
return new Set([...variantFlags, ...unknownFlags]);
|
|
113
|
+
}
|
|
114
|
+
const validFlags = ['A', 'T', 'R', 'D', '-', 'H', 'N'],
|
|
115
|
+
flags = new Set([...this.#flags.filter(flag => validFlags.includes(flag)), ...unknownFlags]);
|
|
116
|
+
if (flags.size === 0) {
|
|
117
|
+
return new Set('S');
|
|
118
|
+
} else if (flags.has('R')) {
|
|
119
|
+
return new Set('R');
|
|
120
|
+
} else if (flags.has('N')) {
|
|
121
|
+
return new Set('N');
|
|
122
|
+
} else if (flags.has('-')) {
|
|
123
|
+
return new Set('-');
|
|
124
|
+
} else if (flags.has('H')) {
|
|
125
|
+
const hasT = flags.has('T'),
|
|
126
|
+
hasD = flags.has('D');
|
|
127
|
+
if (hasT && hasD) {
|
|
128
|
+
return new Set(['+', 'H', 'T', 'D']);
|
|
129
|
+
}
|
|
130
|
+
return new Set(['+', 'H', ...hasT ? ['T'] : [], ...hasD ? ['D'] : [], ...unknownFlags]);
|
|
131
|
+
}
|
|
132
|
+
if (flags.size === 1 && flags.has('T')) {
|
|
133
|
+
flags.add('H');
|
|
134
|
+
}
|
|
135
|
+
if (flags.has('A')) {
|
|
136
|
+
flags.add('+');
|
|
137
|
+
flags.add('S');
|
|
138
|
+
}
|
|
139
|
+
if (flags.has('D')) {
|
|
140
|
+
flags.delete('S');
|
|
141
|
+
}
|
|
142
|
+
return flags;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** @complexity `n` */
|
|
146
|
+
getUnknownFlags() {
|
|
147
|
+
return [...this.getFlags()].filter(flag => /{{.+}}/.test(flag));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** @param {string} flag */
|
|
151
|
+
hasFlag(flag) {
|
|
152
|
+
if (typeof flag !== 'string') {
|
|
153
|
+
this.typeError('hasFlag', 'String');
|
|
154
|
+
}
|
|
155
|
+
return this.#flags.includes(flag);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {string} flag
|
|
160
|
+
* @complexity `n`
|
|
161
|
+
*/
|
|
162
|
+
hasEffectiveFlag(flag) {
|
|
163
|
+
if (typeof flag !== 'string') {
|
|
164
|
+
this.typeError('hasFlag', 'String');
|
|
165
|
+
}
|
|
166
|
+
return this.getEffectiveFlags().has(flag);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @param {string} flag
|
|
171
|
+
* @complexity `n²`
|
|
172
|
+
*/
|
|
173
|
+
removeFlag(flag) {
|
|
174
|
+
for (const token of this.getFlagToken(flag)) {
|
|
175
|
+
token.remove();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @param {string} flag
|
|
181
|
+
* @complexity `n`
|
|
182
|
+
*/
|
|
183
|
+
#newFlag(flag) {
|
|
184
|
+
const token = Parser.run(() => new AtomToken(flag, 'converter-flag', this.getAttribute('config')));
|
|
185
|
+
this.appendChild(token);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @param {string} flag
|
|
190
|
+
* @complexity `n`
|
|
191
|
+
*/
|
|
192
|
+
setFlag(flag) {
|
|
193
|
+
if (!this.#flags.includes(flag)) {
|
|
194
|
+
this.#newFlag(flag);
|
|
195
|
+
} else if (!this.getEffectiveFlags().has(flag)) {
|
|
196
|
+
Parser.error('此 flag 不会生效', flag);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @param {string} flag
|
|
202
|
+
* @complexity `n²`
|
|
203
|
+
*/
|
|
204
|
+
toggleFlag(flag) {
|
|
205
|
+
if (!this.#flags.includes(flag)) {
|
|
206
|
+
this.#newFlag(flag);
|
|
207
|
+
} else {
|
|
208
|
+
this.removeFlag(flag);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
Parser.classes.ConverterFlagsToken = __filename;
|
|
214
|
+
module.exports = ConverterFlagsToken;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {undo} = require('../util/debug'),
|
|
4
|
+
{noWrap} = require('../util/string'),
|
|
5
|
+
/** @type {Parser} */ Parser = require('..'),
|
|
6
|
+
Token = require('.'),
|
|
7
|
+
AtomToken = require('./atom');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 转换规则
|
|
11
|
+
* @classdesc `{childNodes: ...AtomToken)}`
|
|
12
|
+
*/
|
|
13
|
+
class ConverterRuleToken extends Token {
|
|
14
|
+
type = 'converter-rule';
|
|
15
|
+
variant = '';
|
|
16
|
+
unidirectional = false;
|
|
17
|
+
bidirectional = false;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {string} rule
|
|
21
|
+
* @param {accum} accum
|
|
22
|
+
*/
|
|
23
|
+
constructor(rule, hasColon = true, config = Parser.getConfig(), accum = []) {
|
|
24
|
+
super(undefined, config, true, accum, {AtomToken: ':'});
|
|
25
|
+
if (!hasColon) {
|
|
26
|
+
super.insertAt(new AtomToken(rule, 'converter-rule-noconvert', config, accum));
|
|
27
|
+
} else {
|
|
28
|
+
const i = rule.indexOf(':'),
|
|
29
|
+
j = rule.slice(0, i).indexOf('=>'),
|
|
30
|
+
v = (j === -1 ? rule.slice(0, i) : rule.slice(j + 2, i)).trim(),
|
|
31
|
+
{variants} = config;
|
|
32
|
+
if (!variants.includes(v)) {
|
|
33
|
+
super.insertAt(new AtomToken(rule, 'converter-rule-noconvert', config, accum));
|
|
34
|
+
} else {
|
|
35
|
+
super.insertAt(new AtomToken(v, 'converter-rule-variant', config, accum));
|
|
36
|
+
super.insertAt(new AtomToken(rule.slice(i + 1), 'converter-rule-to', config, accum));
|
|
37
|
+
if (j === -1) {
|
|
38
|
+
this.bidirectional = true;
|
|
39
|
+
} else {
|
|
40
|
+
super.insertAt(new AtomToken(rule.slice(0, j), 'converter-rule-from', config, accum), 0);
|
|
41
|
+
this.unidirectional = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
this.seal(['variant', 'unidirectional', 'bidirectional']);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
cloneNode() {
|
|
49
|
+
const cloned = this.cloneChildren(),
|
|
50
|
+
placeholders = ['', ':', '=>:'],
|
|
51
|
+
placeholder = placeholders[cloned.length - 1],
|
|
52
|
+
token = Parser.run(() => new ConverterRuleToken(placeholder, placeholder, this.getAttribute('config')));
|
|
53
|
+
for (let i = 0; i < cloned.length; i++) {
|
|
54
|
+
token.children[i].safeReplaceWith(cloned[i]);
|
|
55
|
+
}
|
|
56
|
+
token.afterBuild();
|
|
57
|
+
return token;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
afterBuild() {
|
|
61
|
+
if (this.childNodes.length > 1) {
|
|
62
|
+
this.setAttribute('variant', this.children.at(-2).text().trim());
|
|
63
|
+
}
|
|
64
|
+
const that = this,
|
|
65
|
+
/** @type {AstListener} */ converterRuleListener = (e, data) => {
|
|
66
|
+
const {childNodes} = that,
|
|
67
|
+
{prevTarget} = e;
|
|
68
|
+
if (childNodes.length > 1 && childNodes.at(-2) === prevTarget) {
|
|
69
|
+
const v = prevTarget.text().trim(),
|
|
70
|
+
{variants} = that.getAttribute('config');
|
|
71
|
+
if (variants.includes(v)) {
|
|
72
|
+
that.setAttribute('variant', v);
|
|
73
|
+
} else {
|
|
74
|
+
undo(e, data);
|
|
75
|
+
throw new Error(`无效的语言变体:${v}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
this.addEventListener(['remove', 'insert', 'text', 'replace'], converterRuleListener);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {number} i
|
|
84
|
+
* @returns {AtomToken}
|
|
85
|
+
*/
|
|
86
|
+
removeAt(i) {
|
|
87
|
+
if (i !== 0 && i !== -this.childNodes.length) {
|
|
88
|
+
throw new RangeError(`${this.constructor.name} 禁止移除第 ${i} 个子节点!`);
|
|
89
|
+
}
|
|
90
|
+
return super.removeAt(i);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
insertAt() {
|
|
94
|
+
throw new Error('转换规则语法复杂,请勿尝试手动插入子节点!');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** @returns {string} */
|
|
98
|
+
toString() {
|
|
99
|
+
if (this.childNodes.length === 3) {
|
|
100
|
+
const [from, variant, to] = this.children;
|
|
101
|
+
return `${from.toString()}=>${variant.toString()}:${to.toString()}`;
|
|
102
|
+
}
|
|
103
|
+
return super.toString(':');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** @param {number} i */
|
|
107
|
+
getGaps(i = 0) {
|
|
108
|
+
const {length} = this.childNodes;
|
|
109
|
+
i = i < 0 ? i + length : i;
|
|
110
|
+
return i === 0 && length === 3 ? 2 : 1;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** @returns {string} */
|
|
114
|
+
text() {
|
|
115
|
+
if (this.childNodes.length === 3) {
|
|
116
|
+
const [from, variant, to] = this.children;
|
|
117
|
+
return `${from.text()}=>${variant.text()}:${to.text()}`;
|
|
118
|
+
}
|
|
119
|
+
return super.text(':');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** @returns {[number, string][]} */
|
|
123
|
+
plain() {
|
|
124
|
+
const {firstElementChild, lastElementChild, unidirectional} = this;
|
|
125
|
+
if (unidirectional) {
|
|
126
|
+
return [...firstElementChild.plain(), ...lastElementChild.plain()];
|
|
127
|
+
}
|
|
128
|
+
return lastElementChild.plain();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
noConvert() {
|
|
132
|
+
for (let i = this.childNodes.length - 2; i >= 0; i--) {
|
|
133
|
+
super.removeAt(i);
|
|
134
|
+
}
|
|
135
|
+
this.setAttribute('unidirectional', false).setAttribute('bidirectional', false).setAttribute('variant', '');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** @param {string} to */
|
|
139
|
+
setTo(to) {
|
|
140
|
+
to = String(to);
|
|
141
|
+
const config = this.getAttribute('config'),
|
|
142
|
+
root = Parser.parse(`-{|${config.variants[0]}:${to}}-`, this.getAttribute('include'), undefined, config),
|
|
143
|
+
{childNodes: {length}, firstElementChild} = root;
|
|
144
|
+
if (length !== 1 || firstElementChild.type !== 'converter' || firstElementChild.childNodes.length !== 2
|
|
145
|
+
|| firstElementChild.lastElementChild.childNodes.length !== 2
|
|
146
|
+
) {
|
|
147
|
+
throw new SyntaxError(`非法的转换目标:${noWrap(to)}`);
|
|
148
|
+
}
|
|
149
|
+
const {lastChild} = firstElementChild.lastElementChild;
|
|
150
|
+
firstElementChild.lastElementChild.removeAt(0);
|
|
151
|
+
this.lastElementChild.safeReplaceWith(lastChild);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** @param {string} variant */
|
|
155
|
+
setVariant(variant) {
|
|
156
|
+
if (typeof variant !== 'string') {
|
|
157
|
+
this.typeError('setVariant', 'String');
|
|
158
|
+
}
|
|
159
|
+
const config = this.getAttribute('config'),
|
|
160
|
+
v = variant.trim();
|
|
161
|
+
if (!config.variants.includes(v)) {
|
|
162
|
+
throw new RangeError(`无效的语言变体:${v}`);
|
|
163
|
+
} else if (this.childNodes.length === 1) {
|
|
164
|
+
super.insertAt(Parser.run(() => new AtomToken(variant, 'converter-rule-variant', config)), 0);
|
|
165
|
+
this.setAttribute('bidirectional', true);
|
|
166
|
+
} else {
|
|
167
|
+
this.children.at(-2).setText(variant);
|
|
168
|
+
}
|
|
169
|
+
this.setAttribute('variant', v);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** @param {string} from */
|
|
173
|
+
setFrom(from) {
|
|
174
|
+
const {variant} = this;
|
|
175
|
+
if (!variant) {
|
|
176
|
+
throw new Error('请先指定语言变体!');
|
|
177
|
+
}
|
|
178
|
+
from = String(from);
|
|
179
|
+
const config = this.getAttribute('config'),
|
|
180
|
+
root = Parser.parse(`-{|${from}=>${variant}:}-`, this.getAttribute('include'), undefined, config),
|
|
181
|
+
{childNodes: {length}, firstElementChild} = root;
|
|
182
|
+
if (length !== 1 || firstElementChild.type !== 'converter' || firstElementChild.childNodes.length !== 2
|
|
183
|
+
|| firstElementChild.lastElementChild.childNodes.length !== 3
|
|
184
|
+
) {
|
|
185
|
+
throw new SyntaxError(`非法的转换原文:${noWrap(from)}`);
|
|
186
|
+
}
|
|
187
|
+
if (this.unidirectional) {
|
|
188
|
+
this.firstElementChild.safeReplaceWith(firstElementChild.lastElementChild.firstChild);
|
|
189
|
+
} else {
|
|
190
|
+
super.insertAt(firstElementChild.lastElementChild.firstChild, 0);
|
|
191
|
+
this.setAttribute('unidirectional', true).setAttribute('bidirectional', false);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** @param {string} from */
|
|
196
|
+
makeUnidirectional(from) {
|
|
197
|
+
this.setFrom(from);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
makeBidirectional() {
|
|
201
|
+
if (this.unidirectional) {
|
|
202
|
+
super.removeAt(0);
|
|
203
|
+
this.setAttribute('unidirectional', false).setAttribute('bidirectional', true);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
Parser.classes.ConverterRuleToken = __filename;
|
|
209
|
+
module.exports = ConverterRuleToken;
|
package/src/extLink.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {noWrap} = require('../util/string'),
|
|
3
|
+
const {noWrap, normalizeSpace} = require('../util/string'),
|
|
4
4
|
/** @type {Parser} */ Parser = require('..'),
|
|
5
5
|
Token = require('.'),
|
|
6
6
|
MagicLinkToken = require('./magicLink');
|
|
@@ -33,9 +33,9 @@ class ExtLinkToken extends Token {
|
|
|
33
33
|
this.appendChild(new MagicLinkToken(url, true, config, accum));
|
|
34
34
|
this.#space = space;
|
|
35
35
|
if (text) {
|
|
36
|
-
const inner = new Token(text, config, true, accum);
|
|
36
|
+
const inner = new Token(text, config, true, accum, {'Stage-7': ':', ConverterToken: ':'});
|
|
37
37
|
inner.type = 'ext-link-text';
|
|
38
|
-
this.appendChild(inner.setAttribute('stage',
|
|
38
|
+
this.appendChild(inner.setAttribute('stage', Parser.MAX_STAGE - 1));
|
|
39
39
|
}
|
|
40
40
|
this.protectChildren(0);
|
|
41
41
|
}
|
|
@@ -50,9 +50,9 @@ class ExtLinkToken extends Token {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
#correct() {
|
|
53
|
-
if (!this.#space
|
|
53
|
+
if (!this.#space && this.childNodes.length > 1
|
|
54
54
|
// 都替换成`<`肯定不对,但无妨
|
|
55
|
-
&& /^[^[\]<>"\x00-\x20\x7f\p{Zs}\ufffd]/u.test(this.
|
|
55
|
+
&& /^[^[\]<>"{\x00-\x20\x7f\p{Zs}\ufffd]/u.test(this.lastElementChild.text().replace(/&[lg]t;/, '<'))
|
|
56
56
|
) {
|
|
57
57
|
this.#space = ' ';
|
|
58
58
|
}
|
|
@@ -60,7 +60,7 @@ class ExtLinkToken extends Token {
|
|
|
60
60
|
|
|
61
61
|
toString() {
|
|
62
62
|
this.#correct();
|
|
63
|
-
return `[${this.firstElementChild.toString()}${this.#space}${this.children[1]
|
|
63
|
+
return `[${this.firstElementChild.toString()}${this.#space}${normalizeSpace(this.children[1])}]`;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
getPadding() {
|
|
@@ -74,7 +74,7 @@ class ExtLinkToken extends Token {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
text() {
|
|
77
|
-
return `[${super.text(' ')}]`;
|
|
77
|
+
return `[${super.text(' ').replaceAll('\n', ' ')}]`;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/**
|
|
@@ -82,7 +82,7 @@ class ExtLinkToken extends Token {
|
|
|
82
82
|
* @complexity `n`
|
|
83
83
|
*/
|
|
84
84
|
plain() {
|
|
85
|
-
return this.
|
|
85
|
+
return this.childNodes.length === 1 ? [] : this.lastElementChild.plain();
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
/** @this {ExtLinkToken & {firstElementChild: MagicLinkToken}} */
|
|
@@ -95,7 +95,7 @@ class ExtLinkToken extends Token {
|
|
|
95
95
|
url = String(url);
|
|
96
96
|
const root = Parser.parse(`[${url}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
|
|
97
97
|
{childNodes: {length}, firstElementChild} = root;
|
|
98
|
-
if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.
|
|
98
|
+
if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.childNodes.length !== 1) {
|
|
99
99
|
throw new SyntaxError(`非法的外链目标:${url}`);
|
|
100
100
|
}
|
|
101
101
|
const {firstChild} = firstElementChild;
|
|
@@ -109,11 +109,11 @@ class ExtLinkToken extends Token {
|
|
|
109
109
|
text = String(text);
|
|
110
110
|
const root = Parser.parse(`[//url ${text}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
|
|
111
111
|
{childNodes: {length}, firstElementChild} = root;
|
|
112
|
-
if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.
|
|
112
|
+
if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.childNodes.length !== 2) {
|
|
113
113
|
throw new SyntaxError(`非法的外链文字:${noWrap(text)}`);
|
|
114
114
|
}
|
|
115
115
|
const {lastChild} = firstElementChild;
|
|
116
|
-
if (this.
|
|
116
|
+
if (this.childNodes.length === 1) {
|
|
117
117
|
this.appendChild(lastChild);
|
|
118
118
|
} else {
|
|
119
119
|
this.lastElementChild.replaceWith(lastChild);
|
package/src/imageParameter.js
CHANGED
|
@@ -213,7 +213,7 @@ class ImageParameterToken extends Token {
|
|
|
213
213
|
{childNodes: {length}, firstElementChild} = root,
|
|
214
214
|
param = firstElementChild?.lastElementChild;
|
|
215
215
|
if (length !== 1 || !firstElementChild?.matches('file#File:F')
|
|
216
|
-
|| firstElementChild.
|
|
216
|
+
|| firstElementChild.childNodes.length !== 2 || param.name !== this.name
|
|
217
217
|
) {
|
|
218
218
|
throw new SyntaxError(`非法的 ${this.name} 参数:${noWrap(value)}`);
|
|
219
219
|
}
|
package/src/index.js
CHANGED
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
* q: QuoteToken
|
|
39
39
|
* w: ExtLinkToken
|
|
40
40
|
* d: ListToken
|
|
41
|
+
* v: ConverterToken
|
|
41
42
|
*/
|
|
42
43
|
|
|
43
44
|
const {externalUse} = require('../util/debug'),
|
|
@@ -202,9 +203,13 @@ class Token extends AstElement {
|
|
|
202
203
|
* @complexity `n`
|
|
203
204
|
*/
|
|
204
205
|
removeAt(i) {
|
|
206
|
+
if (typeof i !== 'number') {
|
|
207
|
+
this.typeError('removeAt', 'Number');
|
|
208
|
+
}
|
|
209
|
+
const iPos = i < 0 ? i + this.childNodes.length : i;
|
|
205
210
|
if (!Parser.running) {
|
|
206
211
|
const protectedIndices = this.#protectedChildren.applyTo(this.childNodes);
|
|
207
|
-
if (protectedIndices.includes(
|
|
212
|
+
if (protectedIndices.includes(iPos)) {
|
|
208
213
|
throw new Error(`${this.constructor.name} 的第 ${i} 个子节点不可移除!`);
|
|
209
214
|
} else if (this.#acceptable) {
|
|
210
215
|
const acceptableIndices = Object.fromEntries(
|
|
@@ -450,6 +455,7 @@ class Token extends AstElement {
|
|
|
450
455
|
this.#parseList();
|
|
451
456
|
break;
|
|
452
457
|
case 10:
|
|
458
|
+
this.#parseConverter();
|
|
453
459
|
// no default
|
|
454
460
|
}
|
|
455
461
|
if (this.type === 'root') {
|
|
@@ -528,25 +534,21 @@ class Token extends AstElement {
|
|
|
528
534
|
return n ? this.build().afterBuild() : this;
|
|
529
535
|
}
|
|
530
536
|
|
|
531
|
-
/** @this {Token & {firstChild: string}} */
|
|
532
537
|
#parseCommentAndExt(includeOnly = false) {
|
|
533
538
|
const parseCommentAndExt = require('../parser/commentAndExt');
|
|
534
539
|
this.setText(parseCommentAndExt(this.firstChild, this.#config, this.#accum, includeOnly));
|
|
535
540
|
}
|
|
536
541
|
|
|
537
|
-
/** @this {Token & {firstChild: string}} */
|
|
538
542
|
#parseBrackets() {
|
|
539
543
|
const parseBrackets = require('../parser/brackets');
|
|
540
544
|
this.setText(parseBrackets(this.firstChild, this.#config, this.#accum));
|
|
541
545
|
}
|
|
542
546
|
|
|
543
|
-
/** @this {Token & {firstChild: string}} */
|
|
544
547
|
#parseHtml() {
|
|
545
548
|
const parseHtml = require('../parser/html');
|
|
546
549
|
this.setText(parseHtml(this.firstChild, this.#config, this.#accum));
|
|
547
550
|
}
|
|
548
551
|
|
|
549
|
-
/** @this {Token & {firstChild: string}} */
|
|
550
552
|
#parseTable() {
|
|
551
553
|
const parseTable = require('../parser/table'),
|
|
552
554
|
TableToken = require('./table');
|
|
@@ -565,13 +567,11 @@ class Token extends AstElement {
|
|
|
565
567
|
}
|
|
566
568
|
}
|
|
567
569
|
|
|
568
|
-
/** @this {Token & {firstChild: string}} */
|
|
569
570
|
#parseHrAndDoubleUndescore() {
|
|
570
571
|
const parseHrAndDoubleUnderscore = require('../parser/hrAndDoubleUnderscore');
|
|
571
572
|
this.setText(parseHrAndDoubleUnderscore(this.firstChild, this.#config, this.#accum));
|
|
572
573
|
}
|
|
573
574
|
|
|
574
|
-
/** @this {Token & {firstChild: string}} */
|
|
575
575
|
#parseLinks() {
|
|
576
576
|
const parseLinks = require('../parser/links');
|
|
577
577
|
this.setText(parseLinks(this.firstChild, this.#config, this.#accum));
|
|
@@ -587,26 +587,30 @@ class Token extends AstElement {
|
|
|
587
587
|
this.setText(lines.join('\n'));
|
|
588
588
|
}
|
|
589
589
|
|
|
590
|
-
/** @this {Token & {firstChild: string}} */
|
|
591
590
|
#parseExternalLinks() {
|
|
592
591
|
const parseExternalLinks = require('../parser/externalLinks');
|
|
593
592
|
this.setText(parseExternalLinks(this.firstChild, this.#config, this.#accum));
|
|
594
593
|
}
|
|
595
594
|
|
|
596
|
-
/** @this {Token & {firstChild: string}} */
|
|
597
595
|
#parseMagicLinks() {
|
|
598
596
|
const parseMagicLinks = require('../parser/magicLinks');
|
|
599
597
|
this.setText(parseMagicLinks(this.firstChild, this.#config, this.#accum));
|
|
600
598
|
}
|
|
601
599
|
|
|
600
|
+
/** @this {Token & {firstChild: string}} */
|
|
602
601
|
#parseList() {
|
|
603
602
|
const parseList = require('../parser/list'),
|
|
604
603
|
lines = this.firstChild.split('\n');
|
|
605
|
-
for (let i = 0; i < lines.length; i++) {
|
|
604
|
+
for (let i = this.type === 'root' ? 0 : 1; i < lines.length; i++) {
|
|
606
605
|
lines[i] = parseList(lines[i], this.#config, this.#accum);
|
|
607
606
|
}
|
|
608
607
|
this.setText(lines.join('\n'));
|
|
609
608
|
}
|
|
609
|
+
|
|
610
|
+
#parseConverter() {
|
|
611
|
+
const parseConverter = require('../parser/converter');
|
|
612
|
+
this.setText(parseConverter(this.firstChild, this.#config, this.#accum));
|
|
613
|
+
}
|
|
610
614
|
}
|
|
611
615
|
|
|
612
616
|
Parser.classes.Token = __filename;
|
package/src/link/file.js
CHANGED
|
@@ -84,7 +84,7 @@ class FileToken extends LinkToken {
|
|
|
84
84
|
* @param {ImageParameterToken} token
|
|
85
85
|
* @complexity `n`
|
|
86
86
|
*/
|
|
87
|
-
insertAt(token, i = this.
|
|
87
|
+
insertAt(token, i = this.childNodes.length) {
|
|
88
88
|
if (!Parser.running) {
|
|
89
89
|
this.getArgs(token.name, false, false).add(token);
|
|
90
90
|
this.#keys.add(token.name);
|
|
@@ -221,7 +221,7 @@ class FileToken extends LinkToken {
|
|
|
221
221
|
root = Parser.parse(wikitext, this.getAttribute('include'), 6, config),
|
|
222
222
|
{childNodes: {length}, firstElementChild} = root;
|
|
223
223
|
if (length !== 1 || !firstElementChild?.matches('file#File:F')
|
|
224
|
-
|| firstElementChild.
|
|
224
|
+
|| firstElementChild.childNodes.length !== 2 || firstElementChild.lastElementChild.name !== key
|
|
225
225
|
) {
|
|
226
226
|
throw new SyntaxError(`非法的 ${key} 参数:${noWrap(value)}`);
|
|
227
227
|
}
|
package/src/link/index.js
CHANGED
|
@@ -29,9 +29,9 @@ class LinkToken extends Token {
|
|
|
29
29
|
'Stage-2': ':', '!ExtToken': '', '!HeadingToken': '',
|
|
30
30
|
}));
|
|
31
31
|
if (linkText !== undefined) {
|
|
32
|
-
const inner = new Token(linkText, config, true, accum);
|
|
32
|
+
const inner = new Token(linkText, config, true, accum, {'Stage-5': ':', ConverterToken: ':'});
|
|
33
33
|
inner.type = 'link-text';
|
|
34
|
-
this.appendChild(inner.setAttribute('stage',
|
|
34
|
+
this.appendChild(inner.setAttribute('stage', Parser.MAX_STAGE - 1));
|
|
35
35
|
}
|
|
36
36
|
this.selfLink = !title.title;
|
|
37
37
|
this.fragment = title.fragment;
|
|
@@ -107,7 +107,7 @@ class LinkToken extends Token {
|
|
|
107
107
|
|
|
108
108
|
/** @returns {[number, string][]} */
|
|
109
109
|
plain() {
|
|
110
|
-
return this.
|
|
110
|
+
return this.childNodes.length === 1 ? [] : this.lastElementChild.plain();
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/** @param {string} link */
|
|
@@ -118,7 +118,7 @@ class LinkToken extends Token {
|
|
|
118
118
|
}
|
|
119
119
|
const root = Parser.parse(`[[${link}]]`, this.getAttribute('include'), 6, this.getAttribute('config')),
|
|
120
120
|
{childNodes: {length}, firstElementChild} = root;
|
|
121
|
-
if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.
|
|
121
|
+
if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.childNodes.length !== 1) {
|
|
122
122
|
const msgs = {link: '内链', file: '文件链接', category: '分类'};
|
|
123
123
|
throw new SyntaxError(`非法的${msgs[this.type]}目标:${link}`);
|
|
124
124
|
}
|
|
@@ -135,7 +135,7 @@ class LinkToken extends Token {
|
|
|
135
135
|
config = this.getAttribute('config'),
|
|
136
136
|
root = Parser.parse(`[[${page ? `:${this.name}` : ''}#${fragment}]]`, include, 6, config),
|
|
137
137
|
{childNodes: {length}, firstElementChild} = root;
|
|
138
|
-
if (length !== 1 || firstElementChild?.type !== 'link' || firstElementChild.
|
|
138
|
+
if (length !== 1 || firstElementChild?.type !== 'link' || firstElementChild.childNodes.length !== 1) {
|
|
139
139
|
throw new SyntaxError(`非法的 fragment:${fragment}`);
|
|
140
140
|
}
|
|
141
141
|
if (page) {
|
|
@@ -169,7 +169,7 @@ class LinkToken extends Token {
|
|
|
169
169
|
this.type === 'category' ? 'Category:' : ''
|
|
170
170
|
}L|${linkText}]]`, this.getAttribute('include'), 6, config),
|
|
171
171
|
{childNodes: {length}, firstElementChild} = root;
|
|
172
|
-
if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.
|
|
172
|
+
if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.childNodes.length !== 2) {
|
|
173
173
|
throw new SyntaxError(`非法的${this.type === 'link' ? '内链文字' : '分类关键字'}:${noWrap(linkText)}`);
|
|
174
174
|
}
|
|
175
175
|
({lastElementChild} = firstElementChild);
|
|
@@ -177,7 +177,7 @@ class LinkToken extends Token {
|
|
|
177
177
|
lastElementChild = Parser.run(() => new Token('', config));
|
|
178
178
|
lastElementChild.setAttribute('stage', 7).type = 'link-text';
|
|
179
179
|
}
|
|
180
|
-
if (this.
|
|
180
|
+
if (this.childNodes.length === 1) {
|
|
181
181
|
this.appendChild(lastElementChild);
|
|
182
182
|
} else {
|
|
183
183
|
this.lastElementChild.safeReplaceWith(lastElementChild);
|