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.
- package/README.md +29 -1931
- package/config/default.json +12 -1
- package/config/llwiki.json +12 -1
- package/config/moegirl.json +9 -1
- package/index.js +42 -0
- package/lib/element.js +2 -1
- package/mixin/sol.js +8 -4
- package/package.json +2 -2
- package/parser/converter.js +44 -0
- package/parser/table.js +7 -3
- package/src/arg.js +6 -6
- package/src/attribute.js +22 -29
- package/src/converter.js +135 -0
- package/src/converterFlags.js +215 -0
- package/src/converterRule.js +210 -0
- package/src/extLink.js +12 -12
- package/src/imageParameter.js +1 -1
- package/src/index.js +19 -16
- package/src/link/category.js +1 -0
- package/src/link/file.js +2 -2
- package/src/link/index.js +7 -7
- package/src/magicLink.js +5 -1
- package/src/nowiki/dd.js +4 -4
- package/src/parameter.js +2 -2
- package/src/syntax.js +3 -1
- package/src/table/td.js +2 -1
- package/src/table/tr.js +1 -1
- package/src/tagPair/index.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 +14 -2
|
@@ -0,0 +1,215 @@
|
|
|
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
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @template {string} T
|
|
47
|
+
* @param {T} key
|
|
48
|
+
* @returns {TokenAttribute<T>}
|
|
49
|
+
*/
|
|
50
|
+
getAttribute(key) {
|
|
51
|
+
if (key === 'flags') {
|
|
52
|
+
return Parser.debugging ? this.#flags : [...this.#flags];
|
|
53
|
+
}
|
|
54
|
+
return super.getAttribute(key);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @param {number} i
|
|
59
|
+
* @complexity `n`
|
|
60
|
+
*/
|
|
61
|
+
removeAt(i) {
|
|
62
|
+
const /** @type {AtomToken} */ token = super.removeAt(i);
|
|
63
|
+
this.#flags?.splice(i, 1);
|
|
64
|
+
return token;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {AtomToken} token
|
|
69
|
+
* @complexity `n`
|
|
70
|
+
*/
|
|
71
|
+
insertAt(token, i = this.childNodes.length) {
|
|
72
|
+
super.insertAt(token, i);
|
|
73
|
+
this.#flags?.splice(i, 0, token.text());
|
|
74
|
+
return token;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
toString() {
|
|
78
|
+
return super.toString(';');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getGaps() {
|
|
82
|
+
return 1;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
text() {
|
|
86
|
+
return super.text(';');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** @returns {[number, string][]} */
|
|
90
|
+
plain() {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @param {string} flag
|
|
96
|
+
* @returns {AtomToken[]}
|
|
97
|
+
* @complexity `n`
|
|
98
|
+
*/
|
|
99
|
+
getFlagToken(flag) {
|
|
100
|
+
return this.#flags.includes(flag) ? this.children.filter(child => child.text().trim() === flag) : [];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getAllFlags() {
|
|
104
|
+
return new Set(this.#flags);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** @complexity `n` */
|
|
108
|
+
getEffectiveFlags() {
|
|
109
|
+
const {variants} = this.getAttribute('config'),
|
|
110
|
+
variantFlags = this.#flags.filter(flag => variants.includes(flag)),
|
|
111
|
+
unknownFlags = this.#flags.filter(flag => /{{.+}}/.test(flag));
|
|
112
|
+
if (variantFlags.length) {
|
|
113
|
+
return new Set([...variantFlags, ...unknownFlags]);
|
|
114
|
+
}
|
|
115
|
+
const validFlags = ['A', 'T', 'R', 'D', '-', 'H', 'N'],
|
|
116
|
+
flags = new Set([...this.#flags.filter(flag => validFlags.includes(flag)), ...unknownFlags]);
|
|
117
|
+
if (flags.size === 0) {
|
|
118
|
+
return new Set('S');
|
|
119
|
+
} else if (flags.has('R')) {
|
|
120
|
+
return new Set('R');
|
|
121
|
+
} else if (flags.has('N')) {
|
|
122
|
+
return new Set('N');
|
|
123
|
+
} else if (flags.has('-')) {
|
|
124
|
+
return new Set('-');
|
|
125
|
+
} else if (flags.has('H')) {
|
|
126
|
+
const hasT = flags.has('T'),
|
|
127
|
+
hasD = flags.has('D');
|
|
128
|
+
if (hasT && hasD) {
|
|
129
|
+
return new Set(['+', 'H', 'T', 'D']);
|
|
130
|
+
}
|
|
131
|
+
return new Set(['+', 'H', ...hasT ? ['T'] : [], ...hasD ? ['D'] : [], ...unknownFlags]);
|
|
132
|
+
}
|
|
133
|
+
if (flags.size === 1 && flags.has('T')) {
|
|
134
|
+
flags.add('H');
|
|
135
|
+
}
|
|
136
|
+
if (flags.has('A')) {
|
|
137
|
+
flags.add('+');
|
|
138
|
+
flags.add('S');
|
|
139
|
+
}
|
|
140
|
+
if (flags.has('D')) {
|
|
141
|
+
flags.delete('S');
|
|
142
|
+
}
|
|
143
|
+
return flags;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** @complexity `n` */
|
|
147
|
+
getUnknownFlags() {
|
|
148
|
+
return [...this.getFlags()].filter(flag => /{{.+}}/.test(flag));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** @param {string} flag */
|
|
152
|
+
hasFlag(flag) {
|
|
153
|
+
if (typeof flag !== 'string') {
|
|
154
|
+
this.typeError('hasFlag', 'String');
|
|
155
|
+
}
|
|
156
|
+
return this.#flags.includes(flag);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @param {string} flag
|
|
161
|
+
* @complexity `n`
|
|
162
|
+
*/
|
|
163
|
+
hasEffectiveFlag(flag) {
|
|
164
|
+
if (typeof flag !== 'string') {
|
|
165
|
+
this.typeError('hasFlag', 'String');
|
|
166
|
+
}
|
|
167
|
+
return this.getEffectiveFlags().has(flag);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @param {string} flag
|
|
172
|
+
* @complexity `n²`
|
|
173
|
+
*/
|
|
174
|
+
removeFlag(flag) {
|
|
175
|
+
for (const token of this.getFlagToken(flag)) {
|
|
176
|
+
token.remove();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* @param {string} flag
|
|
182
|
+
* @complexity `n`
|
|
183
|
+
*/
|
|
184
|
+
#newFlag(flag) {
|
|
185
|
+
const token = Parser.run(() => new AtomToken(flag, 'converter-flag', this.getAttribute('config')));
|
|
186
|
+
this.appendChild(token);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @param {string} flag
|
|
191
|
+
* @complexity `n`
|
|
192
|
+
*/
|
|
193
|
+
setFlag(flag) {
|
|
194
|
+
if (!this.#flags.includes(flag)) {
|
|
195
|
+
this.#newFlag(flag);
|
|
196
|
+
} else if (!this.getEffectiveFlags().has(flag)) {
|
|
197
|
+
Parser.error('此 flag 不会生效', flag);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* @param {string} flag
|
|
203
|
+
* @complexity `n²`
|
|
204
|
+
*/
|
|
205
|
+
toggleFlag(flag) {
|
|
206
|
+
if (!this.#flags.includes(flag)) {
|
|
207
|
+
this.#newFlag(flag);
|
|
208
|
+
} else {
|
|
209
|
+
this.removeFlag(flag);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
Parser.classes.ConverterFlagsToken = __filename;
|
|
215
|
+
module.exports = ConverterFlagsToken;
|
|
@@ -0,0 +1,210 @@
|
|
|
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
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param {number} i
|
|
85
|
+
* @returns {AtomToken}
|
|
86
|
+
*/
|
|
87
|
+
removeAt(i) {
|
|
88
|
+
if (i !== 0 && i !== -this.childNodes.length) {
|
|
89
|
+
throw new RangeError(`${this.constructor.name} 禁止移除第 ${i} 个子节点!`);
|
|
90
|
+
}
|
|
91
|
+
return super.removeAt(i);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
insertAt() {
|
|
95
|
+
throw new Error('转换规则语法复杂,请勿尝试手动插入子节点!');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** @returns {string} */
|
|
99
|
+
toString() {
|
|
100
|
+
if (this.childNodes.length === 3) {
|
|
101
|
+
const [from, variant, to] = this.children;
|
|
102
|
+
return `${from.toString()}=>${variant.toString()}:${to.toString()}`;
|
|
103
|
+
}
|
|
104
|
+
return super.toString(':');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** @param {number} i */
|
|
108
|
+
getGaps(i = 0) {
|
|
109
|
+
const {length} = this.childNodes;
|
|
110
|
+
i = i < 0 ? i + length : i;
|
|
111
|
+
return i === 0 && length === 3 ? 2 : 1;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** @returns {string} */
|
|
115
|
+
text() {
|
|
116
|
+
if (this.childNodes.length === 3) {
|
|
117
|
+
const [from, variant, to] = this.children;
|
|
118
|
+
return `${from.text()}=>${variant.text()}:${to.text()}`;
|
|
119
|
+
}
|
|
120
|
+
return super.text(':');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** @returns {[number, string][]} */
|
|
124
|
+
plain() {
|
|
125
|
+
const {firstElementChild, lastElementChild, unidirectional} = this;
|
|
126
|
+
if (unidirectional) {
|
|
127
|
+
return [...firstElementChild.plain(), ...lastElementChild.plain()];
|
|
128
|
+
}
|
|
129
|
+
return lastElementChild.plain();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
noConvert() {
|
|
133
|
+
for (let i = this.childNodes.length - 2; i >= 0; i--) {
|
|
134
|
+
super.removeAt(i);
|
|
135
|
+
}
|
|
136
|
+
this.setAttribute('unidirectional', false).setAttribute('bidirectional', false).setAttribute('variant', '');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** @param {string} to */
|
|
140
|
+
setTo(to) {
|
|
141
|
+
to = String(to);
|
|
142
|
+
const config = this.getAttribute('config'),
|
|
143
|
+
root = Parser.parse(`-{|${config.variants[0]}:${to}}-`, this.getAttribute('include'), undefined, config),
|
|
144
|
+
{childNodes: {length}, firstElementChild} = root;
|
|
145
|
+
if (length !== 1 || firstElementChild.type !== 'converter' || firstElementChild.childNodes.length !== 2
|
|
146
|
+
|| firstElementChild.lastElementChild.childNodes.length !== 2
|
|
147
|
+
) {
|
|
148
|
+
throw new SyntaxError(`非法的转换目标:${noWrap(to)}`);
|
|
149
|
+
}
|
|
150
|
+
const {lastChild} = firstElementChild.lastElementChild;
|
|
151
|
+
firstElementChild.lastElementChild.removeAt(0);
|
|
152
|
+
this.lastElementChild.safeReplaceWith(lastChild);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** @param {string} variant */
|
|
156
|
+
setVariant(variant) {
|
|
157
|
+
if (typeof variant !== 'string') {
|
|
158
|
+
this.typeError('setVariant', 'String');
|
|
159
|
+
}
|
|
160
|
+
const config = this.getAttribute('config'),
|
|
161
|
+
v = variant.trim();
|
|
162
|
+
if (!config.variants.includes(v)) {
|
|
163
|
+
throw new RangeError(`无效的语言变体:${v}`);
|
|
164
|
+
} else if (this.childNodes.length === 1) {
|
|
165
|
+
super.insertAt(Parser.run(() => new AtomToken(variant, 'converter-rule-variant', config)), 0);
|
|
166
|
+
this.setAttribute('bidirectional', true);
|
|
167
|
+
} else {
|
|
168
|
+
this.children.at(-2).setText(variant);
|
|
169
|
+
}
|
|
170
|
+
this.setAttribute('variant', v);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** @param {string} from */
|
|
174
|
+
setFrom(from) {
|
|
175
|
+
const {variant} = this;
|
|
176
|
+
if (!variant) {
|
|
177
|
+
throw new Error('请先指定语言变体!');
|
|
178
|
+
}
|
|
179
|
+
from = String(from);
|
|
180
|
+
const config = this.getAttribute('config'),
|
|
181
|
+
root = Parser.parse(`-{|${from}=>${variant}:}-`, this.getAttribute('include'), undefined, config),
|
|
182
|
+
{childNodes: {length}, firstElementChild} = root;
|
|
183
|
+
if (length !== 1 || firstElementChild.type !== 'converter' || firstElementChild.childNodes.length !== 2
|
|
184
|
+
|| firstElementChild.lastElementChild.childNodes.length !== 3
|
|
185
|
+
) {
|
|
186
|
+
throw new SyntaxError(`非法的转换原文:${noWrap(from)}`);
|
|
187
|
+
}
|
|
188
|
+
if (this.unidirectional) {
|
|
189
|
+
this.firstElementChild.safeReplaceWith(firstElementChild.lastElementChild.firstChild);
|
|
190
|
+
} else {
|
|
191
|
+
super.insertAt(firstElementChild.lastElementChild.firstChild, 0);
|
|
192
|
+
this.setAttribute('unidirectional', true).setAttribute('bidirectional', false);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** @param {string} from */
|
|
197
|
+
makeUnidirectional(from) {
|
|
198
|
+
this.setFrom(from);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
makeBidirectional() {
|
|
202
|
+
if (this.unidirectional) {
|
|
203
|
+
super.removeAt(0);
|
|
204
|
+
this.setAttribute('unidirectional', false).setAttribute('bidirectional', true);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
Parser.classes.ConverterRuleToken = __filename;
|
|
210
|
+
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,14 +109,14 @@ 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
|
-
this.lastElementChild.
|
|
119
|
+
this.lastElementChild.safeReplaceWith(lastChild);
|
|
120
120
|
}
|
|
121
121
|
this.#space ||= ' ';
|
|
122
122
|
}
|
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'),
|
|
@@ -50,11 +51,8 @@ const {externalUse} = require('../util/debug'),
|
|
|
50
51
|
class Token extends AstElement {
|
|
51
52
|
type = 'root';
|
|
52
53
|
/** 解析阶段,参见顶部注释。只对plain Token有意义。 */ #stage = 0;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* 这个数组起两个作用:1. 数组中的Token会在build时替换`/\x00\d+.\x7f/`标记;2. 数组中的Token会依次执行parseOnce和build方法。
|
|
56
|
-
* @type {accum}
|
|
57
|
-
*/
|
|
54
|
+
#config;
|
|
55
|
+
/** 这个数组起两个作用:1. 数组中的Token会在build时替换`/\x00\d+.\x7f/`标记;2. 数组中的Token会依次执行parseOnce和build方法。 */
|
|
58
56
|
#accum;
|
|
59
57
|
/** @type {Record<string, Ranges>} */ #acceptable;
|
|
60
58
|
#protectedChildren = new Ranges();
|
|
@@ -70,7 +68,9 @@ class Token extends AstElement {
|
|
|
70
68
|
if (typeof wikitext === 'string') {
|
|
71
69
|
this.appendChild(halfParsed ? wikitext : wikitext.replace(/[\x00\x7f]/g, ''));
|
|
72
70
|
}
|
|
73
|
-
this
|
|
71
|
+
this.#config = config;
|
|
72
|
+
this.#accum = accum;
|
|
73
|
+
this.setAttribute('acceptable', acceptable);
|
|
74
74
|
accum.push(this);
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -202,9 +202,13 @@ class Token extends AstElement {
|
|
|
202
202
|
* @complexity `n`
|
|
203
203
|
*/
|
|
204
204
|
removeAt(i) {
|
|
205
|
+
if (typeof i !== 'number') {
|
|
206
|
+
this.typeError('removeAt', 'Number');
|
|
207
|
+
}
|
|
208
|
+
const iPos = i < 0 ? i + this.childNodes.length : i;
|
|
205
209
|
if (!Parser.running) {
|
|
206
210
|
const protectedIndices = this.#protectedChildren.applyTo(this.childNodes);
|
|
207
|
-
if (protectedIndices.includes(
|
|
211
|
+
if (protectedIndices.includes(iPos)) {
|
|
208
212
|
throw new Error(`${this.constructor.name} 的第 ${i} 个子节点不可移除!`);
|
|
209
213
|
} else if (this.#acceptable) {
|
|
210
214
|
const acceptableIndices = Object.fromEntries(
|
|
@@ -450,6 +454,7 @@ class Token extends AstElement {
|
|
|
450
454
|
this.#parseList();
|
|
451
455
|
break;
|
|
452
456
|
case 10:
|
|
457
|
+
this.#parseConverter();
|
|
453
458
|
// no default
|
|
454
459
|
}
|
|
455
460
|
if (this.type === 'root') {
|
|
@@ -528,25 +533,21 @@ class Token extends AstElement {
|
|
|
528
533
|
return n ? this.build().afterBuild() : this;
|
|
529
534
|
}
|
|
530
535
|
|
|
531
|
-
/** @this {Token & {firstChild: string}} */
|
|
532
536
|
#parseCommentAndExt(includeOnly = false) {
|
|
533
537
|
const parseCommentAndExt = require('../parser/commentAndExt');
|
|
534
538
|
this.setText(parseCommentAndExt(this.firstChild, this.#config, this.#accum, includeOnly));
|
|
535
539
|
}
|
|
536
540
|
|
|
537
|
-
/** @this {Token & {firstChild: string}} */
|
|
538
541
|
#parseBrackets() {
|
|
539
542
|
const parseBrackets = require('../parser/brackets');
|
|
540
543
|
this.setText(parseBrackets(this.firstChild, this.#config, this.#accum));
|
|
541
544
|
}
|
|
542
545
|
|
|
543
|
-
/** @this {Token & {firstChild: string}} */
|
|
544
546
|
#parseHtml() {
|
|
545
547
|
const parseHtml = require('../parser/html');
|
|
546
548
|
this.setText(parseHtml(this.firstChild, this.#config, this.#accum));
|
|
547
549
|
}
|
|
548
550
|
|
|
549
|
-
/** @this {Token & {firstChild: string}} */
|
|
550
551
|
#parseTable() {
|
|
551
552
|
const parseTable = require('../parser/table'),
|
|
552
553
|
TableToken = require('./table');
|
|
@@ -565,13 +566,11 @@ class Token extends AstElement {
|
|
|
565
566
|
}
|
|
566
567
|
}
|
|
567
568
|
|
|
568
|
-
/** @this {Token & {firstChild: string}} */
|
|
569
569
|
#parseHrAndDoubleUndescore() {
|
|
570
570
|
const parseHrAndDoubleUnderscore = require('../parser/hrAndDoubleUnderscore');
|
|
571
571
|
this.setText(parseHrAndDoubleUnderscore(this.firstChild, this.#config, this.#accum));
|
|
572
572
|
}
|
|
573
573
|
|
|
574
|
-
/** @this {Token & {firstChild: string}} */
|
|
575
574
|
#parseLinks() {
|
|
576
575
|
const parseLinks = require('../parser/links');
|
|
577
576
|
this.setText(parseLinks(this.firstChild, this.#config, this.#accum));
|
|
@@ -587,26 +586,30 @@ class Token extends AstElement {
|
|
|
587
586
|
this.setText(lines.join('\n'));
|
|
588
587
|
}
|
|
589
588
|
|
|
590
|
-
/** @this {Token & {firstChild: string}} */
|
|
591
589
|
#parseExternalLinks() {
|
|
592
590
|
const parseExternalLinks = require('../parser/externalLinks');
|
|
593
591
|
this.setText(parseExternalLinks(this.firstChild, this.#config, this.#accum));
|
|
594
592
|
}
|
|
595
593
|
|
|
596
|
-
/** @this {Token & {firstChild: string}} */
|
|
597
594
|
#parseMagicLinks() {
|
|
598
595
|
const parseMagicLinks = require('../parser/magicLinks');
|
|
599
596
|
this.setText(parseMagicLinks(this.firstChild, this.#config, this.#accum));
|
|
600
597
|
}
|
|
601
598
|
|
|
599
|
+
/** @this {Token & {firstChild: string}} */
|
|
602
600
|
#parseList() {
|
|
603
601
|
const parseList = require('../parser/list'),
|
|
604
602
|
lines = this.firstChild.split('\n');
|
|
605
|
-
for (let i = 0; i < lines.length; i++) {
|
|
603
|
+
for (let i = this.type === 'root' ? 0 : 1; i < lines.length; i++) {
|
|
606
604
|
lines[i] = parseList(lines[i], this.#config, this.#accum);
|
|
607
605
|
}
|
|
608
606
|
this.setText(lines.join('\n'));
|
|
609
607
|
}
|
|
608
|
+
|
|
609
|
+
#parseConverter() {
|
|
610
|
+
const parseConverter = require('../parser/converter');
|
|
611
|
+
this.setText(parseConverter(this.firstChild, this.#config, this.#accum));
|
|
612
|
+
}
|
|
610
613
|
}
|
|
611
614
|
|
|
612
615
|
Parser.classes.Token = __filename;
|
package/src/link/category.js
CHANGED
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
|
}
|