wikiparser-node 0.0.2 → 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 +226 -7
- 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/errors/README +1 -0
- package/index.js +85 -9
- package/lib/element.js +72 -13
- package/lib/node.js +17 -9
- package/mixin/sol.js +42 -0
- package/package.json +1 -1
- package/parser/converter.js +44 -0
- package/parser/externalLinks.js +1 -1
- package/parser/list.js +58 -0
- package/parser/table.js +2 -2
- package/printed/README +1 -0
- package/src/arg.js +9 -9
- package/src/attribute.js +33 -23
- package/src/converter.js +135 -0
- package/src/converterFlags.js +214 -0
- package/src/converterRule.js +209 -0
- package/src/extLink.js +23 -10
- package/src/heading.js +15 -20
- package/src/html.js +4 -3
- package/src/imageParameter.js +6 -7
- package/src/index.js +38 -23
- package/src/link/file.js +9 -9
- package/src/link/index.js +9 -11
- package/src/magicLink.js +2 -3
- package/src/nowiki/comment.js +1 -1
- package/src/nowiki/dd.js +49 -0
- package/src/nowiki/hr.js +3 -2
- package/src/nowiki/list.js +16 -0
- package/src/parameter.js +5 -5
- package/src/syntax.js +3 -1
- package/src/table/index.js +35 -30
- package/src/table/td.js +3 -2
- package/src/table/tr.js +6 -12
- package/src/tagPair/index.js +1 -1
- package/src/transclude.js +28 -25
- package/tool/index.js +50 -40
- package/typings/index.d.ts +3 -0
- package/typings/node.d.ts +3 -3
- package/typings/token.d.ts +1 -0
- package/util/debug.js +3 -3
- package/util/string.js +16 -1
- package/src/listToken.js +0 -47
|
@@ -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,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const {noWrap, normalizeSpace} = require('../util/string'),
|
|
4
|
+
/** @type {Parser} */ Parser = require('..'),
|
|
4
5
|
Token = require('.'),
|
|
5
6
|
MagicLinkToken = require('./magicLink');
|
|
6
7
|
|
|
@@ -32,9 +33,9 @@ class ExtLinkToken extends Token {
|
|
|
32
33
|
this.appendChild(new MagicLinkToken(url, true, config, accum));
|
|
33
34
|
this.#space = space;
|
|
34
35
|
if (text) {
|
|
35
|
-
const inner = new Token(text, config, true, accum);
|
|
36
|
+
const inner = new Token(text, config, true, accum, {'Stage-7': ':', ConverterToken: ':'});
|
|
36
37
|
inner.type = 'ext-link-text';
|
|
37
|
-
this.appendChild(inner.setAttribute('stage',
|
|
38
|
+
this.appendChild(inner.setAttribute('stage', Parser.MAX_STAGE - 1));
|
|
38
39
|
}
|
|
39
40
|
this.protectChildren(0);
|
|
40
41
|
}
|
|
@@ -48,20 +49,32 @@ class ExtLinkToken extends Token {
|
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
#correct() {
|
|
53
|
+
if (!this.#space && this.childNodes.length > 1
|
|
54
|
+
// 都替换成`<`肯定不对,但无妨
|
|
55
|
+
&& /^[^[\]<>"{\x00-\x20\x7f\p{Zs}\ufffd]/u.test(this.lastElementChild.text().replace(/&[lg]t;/, '<'))
|
|
56
|
+
) {
|
|
57
|
+
this.#space = ' ';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
51
61
|
toString() {
|
|
52
|
-
|
|
62
|
+
this.#correct();
|
|
63
|
+
return `[${this.firstElementChild.toString()}${this.#space}${normalizeSpace(this.children[1])}]`;
|
|
53
64
|
}
|
|
54
65
|
|
|
55
66
|
getPadding() {
|
|
67
|
+
this.#correct();
|
|
56
68
|
return 1;
|
|
57
69
|
}
|
|
58
70
|
|
|
59
71
|
getGaps() {
|
|
72
|
+
this.#correct();
|
|
60
73
|
return this.#space.length;
|
|
61
74
|
}
|
|
62
75
|
|
|
63
76
|
text() {
|
|
64
|
-
return `[${super.text(' ')}]`;
|
|
77
|
+
return `[${super.text(' ').replaceAll('\n', ' ')}]`;
|
|
65
78
|
}
|
|
66
79
|
|
|
67
80
|
/**
|
|
@@ -69,7 +82,7 @@ class ExtLinkToken extends Token {
|
|
|
69
82
|
* @complexity `n`
|
|
70
83
|
*/
|
|
71
84
|
plain() {
|
|
72
|
-
return this.
|
|
85
|
+
return this.childNodes.length === 1 ? [] : this.lastElementChild.plain();
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
/** @this {ExtLinkToken & {firstElementChild: MagicLinkToken}} */
|
|
@@ -82,7 +95,7 @@ class ExtLinkToken extends Token {
|
|
|
82
95
|
url = String(url);
|
|
83
96
|
const root = Parser.parse(`[${url}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
|
|
84
97
|
{childNodes: {length}, firstElementChild} = root;
|
|
85
|
-
if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.
|
|
98
|
+
if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.childNodes.length !== 1) {
|
|
86
99
|
throw new SyntaxError(`非法的外链目标:${url}`);
|
|
87
100
|
}
|
|
88
101
|
const {firstChild} = firstElementChild;
|
|
@@ -96,11 +109,11 @@ class ExtLinkToken extends Token {
|
|
|
96
109
|
text = String(text);
|
|
97
110
|
const root = Parser.parse(`[//url ${text}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
|
|
98
111
|
{childNodes: {length}, firstElementChild} = root;
|
|
99
|
-
if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.
|
|
100
|
-
throw new SyntaxError(`非法的外链文字:${text
|
|
112
|
+
if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.childNodes.length !== 2) {
|
|
113
|
+
throw new SyntaxError(`非法的外链文字:${noWrap(text)}`);
|
|
101
114
|
}
|
|
102
115
|
const {lastChild} = firstElementChild;
|
|
103
|
-
if (this.
|
|
116
|
+
if (this.childNodes.length === 1) {
|
|
104
117
|
this.appendChild(lastChild);
|
|
105
118
|
} else {
|
|
106
119
|
this.lastElementChild.replaceWith(lastChild);
|
package/src/heading.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const fixedToken = require('../mixin/fixedToken'),
|
|
4
|
+
sol = require('../mixin/sol'),
|
|
5
5
|
/** @type {Parser} */ Parser = require('..'),
|
|
6
6
|
Token = require('.');
|
|
7
7
|
|
|
@@ -9,7 +9,7 @@ const {typeError} = require('../util/debug'),
|
|
|
9
9
|
* 章节标题
|
|
10
10
|
* @classdesc `{childNodes: [Token, HiddenToken]}`
|
|
11
11
|
*/
|
|
12
|
-
class HeadingToken extends fixedToken(Token) {
|
|
12
|
+
class HeadingToken extends fixedToken(sol(Token)) {
|
|
13
13
|
type = 'heading';
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -38,34 +38,29 @@ class HeadingToken extends fixedToken(Token) {
|
|
|
38
38
|
return token;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/** @this {HeadingToken & {prependNewLine(): ''|'\n', appendNewLine(): ''|'\n'}} */
|
|
41
42
|
toString() {
|
|
42
|
-
const equals = '='.repeat(Number(this.name))
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|| previousVisibleSibling instanceof Token
|
|
47
|
-
? '\n'
|
|
48
|
-
: ''
|
|
49
|
-
}${equals}${super.toString(equals)}${
|
|
50
|
-
typeof nextVisibleSibling === 'string' && !nextVisibleSibling.startsWith('\n')
|
|
51
|
-
|| nextVisibleSibling instanceof Token
|
|
52
|
-
? '\n'
|
|
53
|
-
: ''
|
|
54
|
-
}`;
|
|
43
|
+
const equals = '='.repeat(Number(this.name));
|
|
44
|
+
return `${this.prependNewLine()}${equals}${
|
|
45
|
+
this.firstElementChild.toString()
|
|
46
|
+
}${equals}${this.lastElementChild.toString()}${this.appendNewLine()}`;
|
|
55
47
|
}
|
|
56
48
|
|
|
57
49
|
getPadding() {
|
|
58
|
-
return Number(this.name);
|
|
50
|
+
return super.getPadding() + Number(this.name);
|
|
59
51
|
}
|
|
60
52
|
|
|
61
53
|
getGaps() {
|
|
62
54
|
return Number(this.name);
|
|
63
55
|
}
|
|
64
56
|
|
|
65
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* @this {HeadingToken & {prependNewLine(): ''|'\n', appendNewLine(): ''|'\n'}}
|
|
59
|
+
* @returns {string}
|
|
60
|
+
*/
|
|
66
61
|
text() {
|
|
67
62
|
const equals = '='.repeat(Number(this.name));
|
|
68
|
-
return `${equals}${this.firstElementChild.text()}${equals}`;
|
|
63
|
+
return `${this.prependNewLine()}${equals}${this.firstElementChild.text()}${equals}${this.appendNewLine()}`;
|
|
69
64
|
}
|
|
70
65
|
|
|
71
66
|
/** @returns {[number, string][]} */
|
|
@@ -76,7 +71,7 @@ class HeadingToken extends fixedToken(Token) {
|
|
|
76
71
|
/** @param {number} n */
|
|
77
72
|
setLevel(n) {
|
|
78
73
|
if (typeof n !== 'number') {
|
|
79
|
-
typeError(
|
|
74
|
+
this.typeError('setLevel', 'Number');
|
|
80
75
|
}
|
|
81
76
|
n = Math.min(Math.max(n, 1), 6);
|
|
82
77
|
this.setAttribute('name', String(n)).firstElementChild.setAttribute('name', this.name);
|
package/src/html.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const {noWrap} = require('../util/string'),
|
|
4
|
+
fixedToken = require('../mixin/fixedToken'),
|
|
4
5
|
attributeParent = require('../mixin/attributeParent'),
|
|
5
6
|
/** @type {Parser} */ Parser = require('..'),
|
|
6
7
|
Token = require('.');
|
|
@@ -79,7 +80,7 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
|
|
|
79
80
|
findMatchingTag() {
|
|
80
81
|
const {html} = this.getAttribute('config'),
|
|
81
82
|
{name, parentElement, closing, selfClosing} = this,
|
|
82
|
-
string = this.toString()
|
|
83
|
+
string = noWrap(this.toString());
|
|
83
84
|
if (closing && selfClosing) {
|
|
84
85
|
throw new SyntaxError(`同时闭合和自封闭的标签:${string}`);
|
|
85
86
|
} else if (html[2].includes(name) || selfClosing && html[1].includes(name)) { // 自封闭标签
|
|
@@ -134,7 +135,7 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
|
|
|
134
135
|
this.selfClosing = false;
|
|
135
136
|
this.closing = true;
|
|
136
137
|
} else {
|
|
137
|
-
Parser.warn('无法修复无效自封闭标签', this.toString()
|
|
138
|
+
Parser.warn('无法修复无效自封闭标签', noWrap(this.toString()));
|
|
138
139
|
throw new Error(`无法修复无效自封闭标签:前文共有 ${imbalance} 个未匹配的闭合标签`);
|
|
139
140
|
}
|
|
140
141
|
}
|
package/src/imageParameter.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
{text, extUrlChar} = require('../util/string'),
|
|
3
|
+
const {text, noWrap, extUrlChar} = require('../util/string'),
|
|
5
4
|
Title = require('../lib/title'),
|
|
6
5
|
/** @type {Parser} */ Parser = require('..'),
|
|
7
6
|
Token = require('.');
|
|
@@ -32,7 +31,7 @@ class ImageParameterToken extends Token {
|
|
|
32
31
|
if (!value) {
|
|
33
32
|
return this.#noLink;
|
|
34
33
|
}
|
|
35
|
-
const regex = new RegExp(`(?:${config.protocol}|//)${extUrlChar}`, 'ui');
|
|
34
|
+
const regex = new RegExp(`(?:${config.protocol}|//)${extUrlChar}(?=\x00\\d+t\x7f|$)`, 'ui');
|
|
36
35
|
if (regex.test(value)) {
|
|
37
36
|
return value;
|
|
38
37
|
}
|
|
@@ -200,13 +199,13 @@ class ImageParameterToken extends Token {
|
|
|
200
199
|
setValue(value) {
|
|
201
200
|
if (this.#isVoid()) {
|
|
202
201
|
if (typeof value !== 'boolean') {
|
|
203
|
-
typeError(
|
|
202
|
+
this.typeError('setValue', 'Boolean');
|
|
204
203
|
} else if (value === false) {
|
|
205
204
|
this.remove();
|
|
206
205
|
}
|
|
207
206
|
return;
|
|
208
207
|
} else if (typeof value !== 'string') {
|
|
209
|
-
typeError(
|
|
208
|
+
this.typeError('setValue', 'String');
|
|
210
209
|
}
|
|
211
210
|
const root = Parser.parse(`[[File:F|${
|
|
212
211
|
this.#syntax ? this.#syntax.replace('$1', value) : value
|
|
@@ -214,9 +213,9 @@ class ImageParameterToken extends Token {
|
|
|
214
213
|
{childNodes: {length}, firstElementChild} = root,
|
|
215
214
|
param = firstElementChild?.lastElementChild;
|
|
216
215
|
if (length !== 1 || !firstElementChild?.matches('file#File:F')
|
|
217
|
-
|| firstElementChild.
|
|
216
|
+
|| firstElementChild.childNodes.length !== 2 || param.name !== this.name
|
|
218
217
|
) {
|
|
219
|
-
throw new SyntaxError(`非法的 ${this.name} 参数:${value
|
|
218
|
+
throw new SyntaxError(`非法的 ${this.name} 参数:${noWrap(value)}`);
|
|
220
219
|
}
|
|
221
220
|
this.replaceChildren(...param.childNodes);
|
|
222
221
|
}
|