wikiparser-node 0.5.0 → 0.6.1
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/config/default.json +129 -66
- package/config/zhwiki.json +4 -4
- package/index.js +74 -65
- package/lib/element.js +125 -152
- package/lib/node.js +251 -223
- package/lib/ranges.js +2 -2
- package/lib/text.js +64 -64
- package/lib/title.js +8 -7
- package/mixin/hidden.js +2 -0
- package/mixin/sol.js +1 -2
- package/package.json +4 -3
- package/parser/brackets.js +8 -2
- package/parser/externalLinks.js +1 -1
- package/parser/hrAndDoubleUnderscore.js +4 -4
- package/parser/links.js +7 -7
- package/parser/table.js +12 -10
- package/src/arg.js +53 -48
- package/src/atom/index.js +7 -5
- package/src/attribute.js +91 -80
- package/src/charinsert.js +91 -0
- package/src/converter.js +22 -11
- package/src/converterFlags.js +72 -62
- package/src/converterRule.js +49 -49
- package/src/extLink.js +30 -28
- package/src/gallery.js +56 -32
- package/src/hasNowiki/index.js +42 -0
- package/src/hasNowiki/pre.js +40 -0
- package/src/heading.js +15 -11
- package/src/html.js +38 -38
- package/src/imageParameter.js +64 -48
- package/src/imagemap.js +205 -0
- package/src/imagemapLink.js +43 -0
- package/src/index.js +222 -124
- package/src/link/category.js +4 -8
- package/src/link/file.js +95 -59
- package/src/link/galleryImage.js +74 -10
- package/src/link/index.js +61 -39
- package/src/magicLink.js +21 -22
- package/src/nested/choose.js +24 -0
- package/src/nested/combobox.js +23 -0
- package/src/nested/index.js +88 -0
- package/src/nested/references.js +23 -0
- package/src/nowiki/comment.js +17 -17
- package/src/nowiki/dd.js +2 -2
- package/src/nowiki/doubleUnderscore.js +14 -14
- package/src/nowiki/index.js +12 -0
- package/src/onlyinclude.js +10 -8
- package/src/paramTag/index.js +83 -0
- package/src/paramTag/inputbox.js +42 -0
- package/src/parameter.js +32 -18
- package/src/syntax.js +9 -1
- package/src/table/index.js +33 -32
- package/src/table/td.js +51 -57
- package/src/table/tr.js +6 -6
- package/src/tagPair/ext.js +58 -40
- package/src/tagPair/include.js +1 -1
- package/src/tagPair/index.js +21 -20
- package/src/transclude.js +158 -143
- package/tool/index.js +720 -439
- package/util/base.js +17 -0
- package/util/debug.js +1 -1
- package/util/diff.js +1 -1
- package/util/string.js +20 -20
package/src/transclude.js
CHANGED
|
@@ -14,8 +14,13 @@ const {removeComment, escapeRegExp, text, noWrap, print} = require('../util/stri
|
|
|
14
14
|
class TranscludeToken extends Token {
|
|
15
15
|
type = 'template';
|
|
16
16
|
modifier = '';
|
|
17
|
-
/** @type {Set<string>} */ #keys = new Set();
|
|
18
17
|
/** @type {Record<string, Set<ParameterToken>>} */ #args = {};
|
|
18
|
+
/** @type {Set<string>} */ #keys = new Set();
|
|
19
|
+
|
|
20
|
+
/** 是否存在重复参数 */
|
|
21
|
+
get duplication() {
|
|
22
|
+
return this.isTemplate() && Boolean(this.hasDuplicatedArgs());
|
|
23
|
+
}
|
|
19
24
|
|
|
20
25
|
/**
|
|
21
26
|
* 设置引用修饰符
|
|
@@ -69,7 +74,7 @@ class TranscludeToken extends Token {
|
|
|
69
74
|
token = new SyntaxToken(magicWord, pattern, 'magic-word-name', config, accum, {
|
|
70
75
|
'Stage-1': ':', '!ExtToken': '',
|
|
71
76
|
});
|
|
72
|
-
this.
|
|
77
|
+
this.insertAt(token);
|
|
73
78
|
if (arg.length > 0) {
|
|
74
79
|
parts.unshift([arg.join(':')]);
|
|
75
80
|
}
|
|
@@ -83,7 +88,7 @@ class TranscludeToken extends Token {
|
|
|
83
88
|
const invoke = new AtomToken(part.join('='), `invoke-${
|
|
84
89
|
i ? 'function' : 'module'
|
|
85
90
|
}`, config, accum, {'Stage-1': ':', '!ExtToken': ''});
|
|
86
|
-
this.
|
|
91
|
+
this.insertAt(invoke);
|
|
87
92
|
}
|
|
88
93
|
this.getAttribute('protectChildren')('1:3');
|
|
89
94
|
}
|
|
@@ -95,9 +100,8 @@ class TranscludeToken extends Token {
|
|
|
95
100
|
accum.pop();
|
|
96
101
|
throw new SyntaxError(`非法的模板名称:${name}`);
|
|
97
102
|
}
|
|
98
|
-
this.setAttribute('name', this.normalizeTitle(name, 10, true).title);
|
|
99
103
|
const token = new AtomToken(title, 'template-name', config, accum, {'Stage-2': ':', '!HeadingToken': ''});
|
|
100
|
-
this.
|
|
104
|
+
this.insertAt(token);
|
|
101
105
|
}
|
|
102
106
|
const templateLike = this.isTemplate();
|
|
103
107
|
let i = 1;
|
|
@@ -110,85 +114,11 @@ class TranscludeToken extends Token {
|
|
|
110
114
|
part.unshift(i);
|
|
111
115
|
i++;
|
|
112
116
|
}
|
|
113
|
-
this.
|
|
117
|
+
this.insertAt(new ParameterToken(...part, config, accum));
|
|
114
118
|
}
|
|
115
119
|
this.getAttribute('protectChildren')(0);
|
|
116
120
|
}
|
|
117
121
|
|
|
118
|
-
/** @override */
|
|
119
|
-
cloneNode() {
|
|
120
|
-
const [first, ...cloned] = this.cloneChildNodes(),
|
|
121
|
-
config = this.getAttribute('config');
|
|
122
|
-
return Parser.run(() => {
|
|
123
|
-
const token = new TranscludeToken(this.type === 'template' ? '' : first.text(), [], config);
|
|
124
|
-
token.setModifier(this.modifier);
|
|
125
|
-
token.firstChild.safeReplaceWith(first);
|
|
126
|
-
token.afterBuild();
|
|
127
|
-
token.append(...cloned);
|
|
128
|
-
return token;
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** @override */
|
|
133
|
-
afterBuild() {
|
|
134
|
-
if (this.name.includes('\0')) {
|
|
135
|
-
this.setAttribute('name', text(this.getAttribute('buildFromStr')(this.name)));
|
|
136
|
-
}
|
|
137
|
-
if (this.isTemplate()) {
|
|
138
|
-
/**
|
|
139
|
-
* 当事件bubble到`parameter`时,将`oldKey`和`newKey`保存进AstEventData。
|
|
140
|
-
* 当继续bubble到`template`时,处理并删除`oldKey`和`newKey`。
|
|
141
|
-
* @type {AstListener}
|
|
142
|
-
*/
|
|
143
|
-
const transcludeListener = (e, data) => {
|
|
144
|
-
const {prevTarget} = e,
|
|
145
|
-
{oldKey, newKey} = data ?? {};
|
|
146
|
-
if (typeof oldKey === 'string') {
|
|
147
|
-
delete data.oldKey;
|
|
148
|
-
delete data.newKey;
|
|
149
|
-
}
|
|
150
|
-
if (prevTarget === this.firstChild && this.type === 'template') {
|
|
151
|
-
this.setAttribute('name', this.normalizeTitle(prevTarget.text(), 10).title);
|
|
152
|
-
} else if (oldKey !== newKey && prevTarget instanceof ParameterToken) {
|
|
153
|
-
const oldArgs = this.getArgs(oldKey, false, false);
|
|
154
|
-
oldArgs.delete(prevTarget);
|
|
155
|
-
this.getArgs(newKey, false, false).add(prevTarget);
|
|
156
|
-
this.#keys.add(newKey);
|
|
157
|
-
if (oldArgs.size === 0) {
|
|
158
|
-
this.#keys.delete(oldKey);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
this.addEventListener(['remove', 'insert', 'replace', 'text'], transcludeListener);
|
|
163
|
-
}
|
|
164
|
-
return this;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/** 替换引用 */
|
|
168
|
-
subst() {
|
|
169
|
-
this.setModifier('subst');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/** 安全的替换引用 */
|
|
173
|
-
safesubst() {
|
|
174
|
-
this.setModifier('safesubst');
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* @override
|
|
179
|
-
* @template {string} T
|
|
180
|
-
* @param {T} key 属性键
|
|
181
|
-
* @returns {TokenAttribute<T>}
|
|
182
|
-
*/
|
|
183
|
-
getAttribute(key) {
|
|
184
|
-
if (key === 'args') {
|
|
185
|
-
return {...this.#args};
|
|
186
|
-
} else if (key === 'keys') {
|
|
187
|
-
return !Parser.debugging && externalUse('getAttribute') ? new Set(this.#keys) : this.#keys;
|
|
188
|
-
}
|
|
189
|
-
return super.getAttribute(key);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
122
|
/**
|
|
193
123
|
* @override
|
|
194
124
|
* @param {string} selector
|
|
@@ -249,20 +179,6 @@ class TranscludeToken extends Token {
|
|
|
249
179
|
return this.type === 'template' || this.type === 'magic-word' && this.name === 'invoke';
|
|
250
180
|
}
|
|
251
181
|
|
|
252
|
-
/**
|
|
253
|
-
* @override
|
|
254
|
-
* @returns {string}
|
|
255
|
-
* @complexity `n`
|
|
256
|
-
*/
|
|
257
|
-
text() {
|
|
258
|
-
const {childNodes, firstChild, modifier} = this;
|
|
259
|
-
return `{{${modifier}${modifier && ':'}${
|
|
260
|
-
this.type === 'magic-word'
|
|
261
|
-
? `${firstChild.text()}${childNodes.length > 1 ? ':' : ''}${text(childNodes.slice(1), '|')}`
|
|
262
|
-
: super.text('|')
|
|
263
|
-
}}}`;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
182
|
/**
|
|
267
183
|
* 处理匿名参数更改
|
|
268
184
|
* @param {number|ParameterToken} addedToken 新增的参数
|
|
@@ -291,25 +207,6 @@ class TranscludeToken extends Token {
|
|
|
291
207
|
}
|
|
292
208
|
}
|
|
293
209
|
|
|
294
|
-
/**
|
|
295
|
-
* @override
|
|
296
|
-
* @param {number} i 移除位置
|
|
297
|
-
* @complexity `n`
|
|
298
|
-
*/
|
|
299
|
-
removeAt(i) {
|
|
300
|
-
const /** @type {ParameterToken} */ token = super.removeAt(i);
|
|
301
|
-
if (token.anon) {
|
|
302
|
-
this.#handleAnonArgChange(Number(token.name));
|
|
303
|
-
} else {
|
|
304
|
-
const args = this.getArgs(token.name, false, false);
|
|
305
|
-
args.delete(token);
|
|
306
|
-
if (args.size === 0) {
|
|
307
|
-
this.#keys.delete(token.name);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
return token;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
210
|
/**
|
|
314
211
|
* @override
|
|
315
212
|
* @param {ParameterToken} token 待插入的子节点
|
|
@@ -357,8 +254,10 @@ class TranscludeToken extends Token {
|
|
|
357
254
|
}
|
|
358
255
|
copy ||= !Parser.debugging && externalUse('getArgs');
|
|
359
256
|
const keyStr = String(key).trim();
|
|
360
|
-
let args
|
|
361
|
-
if (
|
|
257
|
+
let args;
|
|
258
|
+
if (Object.hasOwn(this.#args, keyStr)) {
|
|
259
|
+
args = this.#args[keyStr];
|
|
260
|
+
} else {
|
|
362
261
|
args = new Set(this.getAllArgs().filter(({name}) => keyStr === name));
|
|
363
262
|
this.#args[keyStr] = args;
|
|
364
263
|
}
|
|
@@ -370,6 +269,135 @@ class TranscludeToken extends Token {
|
|
|
370
269
|
return args;
|
|
371
270
|
}
|
|
372
271
|
|
|
272
|
+
/**
|
|
273
|
+
* 获取重名参数
|
|
274
|
+
* @complexity `n`
|
|
275
|
+
* @returns {[string, Set<ParameterToken>][]}
|
|
276
|
+
* @throws `Error` 仅用于模板
|
|
277
|
+
*/
|
|
278
|
+
getDuplicatedArgs() {
|
|
279
|
+
if (this.isTemplate()) {
|
|
280
|
+
return Object.entries(this.#args).filter(([, {size}]) => size > 1)
|
|
281
|
+
.map(([key, args]) => [key, new Set(args)]);
|
|
282
|
+
}
|
|
283
|
+
throw new Error(`${this.constructor.name}.getDuplicatedArgs 方法仅供模板使用!`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** @override */
|
|
287
|
+
cloneNode() {
|
|
288
|
+
const [first, ...cloned] = this.cloneChildNodes(),
|
|
289
|
+
config = this.getAttribute('config');
|
|
290
|
+
return Parser.run(() => {
|
|
291
|
+
const token = new TranscludeToken(this.type === 'template' ? '' : first.text(), [], config);
|
|
292
|
+
token.setModifier(this.modifier);
|
|
293
|
+
token.firstChild.safeReplaceWith(first);
|
|
294
|
+
token.afterBuild();
|
|
295
|
+
token.append(...cloned);
|
|
296
|
+
return token;
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** @override */
|
|
301
|
+
afterBuild() {
|
|
302
|
+
if (this.type === 'template') {
|
|
303
|
+
this.setAttribute('name', this.normalizeTitle(this.firstChild.text(), 10).title);
|
|
304
|
+
}
|
|
305
|
+
if (this.isTemplate()) {
|
|
306
|
+
/**
|
|
307
|
+
* 当事件bubble到`parameter`时,将`oldKey`和`newKey`保存进AstEventData。
|
|
308
|
+
* 当继续bubble到`template`时,处理并删除`oldKey`和`newKey`。
|
|
309
|
+
* @type {AstListener}
|
|
310
|
+
*/
|
|
311
|
+
const transcludeListener = (e, data) => {
|
|
312
|
+
const {prevTarget} = e,
|
|
313
|
+
{oldKey, newKey} = data ?? {};
|
|
314
|
+
if (typeof oldKey === 'string') {
|
|
315
|
+
delete data.oldKey;
|
|
316
|
+
delete data.newKey;
|
|
317
|
+
}
|
|
318
|
+
if (prevTarget === this.firstChild && this.type === 'template') {
|
|
319
|
+
this.setAttribute('name', this.normalizeTitle(prevTarget.text(), 10).title);
|
|
320
|
+
} else if (oldKey !== newKey && prevTarget instanceof ParameterToken) {
|
|
321
|
+
const oldArgs = this.getArgs(oldKey, false, false);
|
|
322
|
+
oldArgs.delete(prevTarget);
|
|
323
|
+
this.getArgs(newKey, false, false).add(prevTarget);
|
|
324
|
+
this.#keys.add(newKey);
|
|
325
|
+
if (oldArgs.size === 0) {
|
|
326
|
+
this.#keys.delete(oldKey);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
this.addEventListener(['remove', 'insert', 'replace', 'text'], transcludeListener);
|
|
331
|
+
}
|
|
332
|
+
return this;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** 替换引用 */
|
|
336
|
+
subst() {
|
|
337
|
+
this.setModifier('subst');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** 安全的替换引用 */
|
|
341
|
+
safesubst() {
|
|
342
|
+
this.setModifier('safesubst');
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* @override
|
|
347
|
+
* @param {PropertyKey} key 属性键
|
|
348
|
+
*/
|
|
349
|
+
hasAttribute(key) {
|
|
350
|
+
return key === 'keys' || super.hasAttribute(key);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* @override
|
|
355
|
+
* @template {string} T
|
|
356
|
+
* @param {T} key 属性键
|
|
357
|
+
* @returns {TokenAttribute<T>}
|
|
358
|
+
*/
|
|
359
|
+
getAttribute(key) {
|
|
360
|
+
if (key === 'args') {
|
|
361
|
+
return {...this.#args};
|
|
362
|
+
} else if (key === 'keys') {
|
|
363
|
+
return !Parser.debugging && externalUse('getAttribute') ? new Set(this.#keys) : this.#keys;
|
|
364
|
+
}
|
|
365
|
+
return super.getAttribute(key);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* @override
|
|
370
|
+
* @returns {string}
|
|
371
|
+
* @complexity `n`
|
|
372
|
+
*/
|
|
373
|
+
text() {
|
|
374
|
+
const {childNodes, firstChild, modifier} = this;
|
|
375
|
+
return `{{${modifier}${modifier && ':'}${
|
|
376
|
+
this.type === 'magic-word'
|
|
377
|
+
? `${firstChild.text()}${childNodes.length > 1 ? ':' : ''}${text(childNodes.slice(1), '|')}`
|
|
378
|
+
: super.text('|')
|
|
379
|
+
}}}`;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* @override
|
|
384
|
+
* @param {number} i 移除位置
|
|
385
|
+
* @complexity `n`
|
|
386
|
+
*/
|
|
387
|
+
removeAt(i) {
|
|
388
|
+
const /** @type {ParameterToken} */ token = super.removeAt(i);
|
|
389
|
+
if (token.anon) {
|
|
390
|
+
this.#handleAnonArgChange(Number(token.name));
|
|
391
|
+
} else {
|
|
392
|
+
const args = this.getArgs(token.name, false, false);
|
|
393
|
+
args.delete(token);
|
|
394
|
+
if (args.size === 0) {
|
|
395
|
+
this.#keys.delete(token.name);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return token;
|
|
399
|
+
}
|
|
400
|
+
|
|
373
401
|
/**
|
|
374
402
|
* 是否具有某参数
|
|
375
403
|
* @param {string|number} key 参数名
|
|
@@ -431,7 +459,7 @@ class TranscludeToken extends Token {
|
|
|
431
459
|
* 获取生效的参数值
|
|
432
460
|
* @template {string|number|undefined} T
|
|
433
461
|
* @param {T} key 参数名
|
|
434
|
-
* @returns {T extends undefined ?
|
|
462
|
+
* @returns {T extends undefined ? Record<string, string> : string}
|
|
435
463
|
* @complexity `n`
|
|
436
464
|
*/
|
|
437
465
|
getValue(key) {
|
|
@@ -452,13 +480,13 @@ class TranscludeToken extends Token {
|
|
|
452
480
|
const templateLike = this.isTemplate(),
|
|
453
481
|
wikitext = `{{${templateLike ? ':T|' : 'lc:'}${val}}}`,
|
|
454
482
|
root = Parser.parse(wikitext, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
455
|
-
{
|
|
483
|
+
{length, firstChild: transclude} = root,
|
|
456
484
|
/** @type {Token & {lastChild: ParameterToken}} */
|
|
457
|
-
{type, name,
|
|
485
|
+
{type, name, length: transcludeLength, lastChild} = transclude,
|
|
458
486
|
targetType = templateLike ? 'template' : 'magic-word',
|
|
459
487
|
targetName = templateLike ? 'T' : 'lc';
|
|
460
488
|
if (length === 1 && type === targetType && name === targetName && transcludeLength === 2 && lastChild.anon) {
|
|
461
|
-
return this.
|
|
489
|
+
return this.insertAt(lastChild);
|
|
462
490
|
}
|
|
463
491
|
throw new SyntaxError(`非法的匿名参数:${noWrap(val)}`);
|
|
464
492
|
}
|
|
@@ -485,12 +513,12 @@ class TranscludeToken extends Token {
|
|
|
485
513
|
}
|
|
486
514
|
const wikitext = `{{:T|${key}=${value}}}`,
|
|
487
515
|
root = Parser.parse(wikitext, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
488
|
-
{
|
|
489
|
-
{type, name,
|
|
516
|
+
{length, firstChild: template} = root,
|
|
517
|
+
{type, name, length: templateLength, lastChild: parameter} = template;
|
|
490
518
|
if (length !== 1 || type !== 'template' || name !== 'T' || templateLength !== 2 || parameter.name !== key) {
|
|
491
519
|
throw new SyntaxError(`非法的命名参数:${key}=${noWrap(value)}`);
|
|
492
520
|
}
|
|
493
|
-
this.
|
|
521
|
+
this.insertAt(parameter);
|
|
494
522
|
}
|
|
495
523
|
|
|
496
524
|
/**
|
|
@@ -520,7 +548,7 @@ class TranscludeToken extends Token {
|
|
|
520
548
|
this.typeError('replaceTemplate', 'String');
|
|
521
549
|
}
|
|
522
550
|
const root = Parser.parse(`{{${title}}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
523
|
-
{
|
|
551
|
+
{length, firstChild: template} = root;
|
|
524
552
|
if (length !== 1 || template.type !== 'template' || template.childNodes.length !== 1) {
|
|
525
553
|
throw new SyntaxError(`非法的模板名称:${title}`);
|
|
526
554
|
}
|
|
@@ -540,15 +568,15 @@ class TranscludeToken extends Token {
|
|
|
540
568
|
this.typeError('replaceModule', 'String');
|
|
541
569
|
}
|
|
542
570
|
const root = Parser.parse(`{{#invoke:${title}}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
543
|
-
{
|
|
544
|
-
{type, name,
|
|
571
|
+
{length, firstChild: invoke} = root,
|
|
572
|
+
{type, name, length: invokeLength, lastChild} = invoke;
|
|
545
573
|
if (length !== 1 || type !== 'magic-word' || name !== 'invoke' || invokeLength !== 2) {
|
|
546
574
|
throw new SyntaxError(`非法的模块名称:${title}`);
|
|
547
575
|
} else if (this.childNodes.length > 1) {
|
|
548
576
|
this.childNodes[1].replaceChildren(...lastChild.childNodes);
|
|
549
577
|
} else {
|
|
550
578
|
invoke.destroy(true);
|
|
551
|
-
this.
|
|
579
|
+
this.insertAt(lastChild);
|
|
552
580
|
}
|
|
553
581
|
}
|
|
554
582
|
|
|
@@ -570,15 +598,15 @@ class TranscludeToken extends Token {
|
|
|
570
598
|
const root = Parser.parse(
|
|
571
599
|
`{{#invoke:M|${func}}}`, this.getAttribute('include'), 2, this.getAttribute('config'),
|
|
572
600
|
),
|
|
573
|
-
{
|
|
574
|
-
{type, name,
|
|
601
|
+
{length, firstChild: invoke} = root,
|
|
602
|
+
{type, name, length: invokeLength, lastChild} = invoke;
|
|
575
603
|
if (length !== 1 || type !== 'magic-word' || name !== 'invoke' || invokeLength !== 3) {
|
|
576
604
|
throw new SyntaxError(`非法的模块函数名:${func}`);
|
|
577
605
|
} else if (this.childNodes.length > 2) {
|
|
578
606
|
this.childNodes[2].replaceChildren(...lastChild.childNodes);
|
|
579
607
|
} else {
|
|
580
608
|
invoke.destroy(true);
|
|
581
|
-
this.
|
|
609
|
+
this.insertAt(lastChild);
|
|
582
610
|
}
|
|
583
611
|
}
|
|
584
612
|
|
|
@@ -594,19 +622,6 @@ class TranscludeToken extends Token {
|
|
|
594
622
|
throw new Error(`${this.constructor.name}.hasDuplicatedArgs 方法仅供模板使用!`);
|
|
595
623
|
}
|
|
596
624
|
|
|
597
|
-
/**
|
|
598
|
-
* 获取重名参数
|
|
599
|
-
* @complexity `n`
|
|
600
|
-
* @returns {[string, Set<ParameterToken>][]}
|
|
601
|
-
* @throws `Error` 仅用于模板
|
|
602
|
-
*/
|
|
603
|
-
getDuplicatedArgs() {
|
|
604
|
-
if (this.isTemplate()) {
|
|
605
|
-
return Object.entries(this.#args).filter(([, {size}]) => size > 1).map(([key, args]) => [key, new Set(args)]);
|
|
606
|
-
}
|
|
607
|
-
throw new Error(`${this.constructor.name}.getDuplicatedArgs 方法仅供模板使用!`);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
625
|
/**
|
|
611
626
|
* 修复重名参数:
|
|
612
627
|
* `aggressive = false`时只移除空参数和全同参数,优先保留匿名参数,否则将所有匿名参数更改为命名。
|
|
@@ -627,7 +642,7 @@ class TranscludeToken extends Token {
|
|
|
627
642
|
const /** @type {Record<string, ParameterToken[]>} */ values = {};
|
|
628
643
|
for (const arg of args) {
|
|
629
644
|
const val = arg.getValue().trim();
|
|
630
|
-
if (val
|
|
645
|
+
if (Object.hasOwn(values, val)) {
|
|
631
646
|
values[val].push(arg);
|
|
632
647
|
} else {
|
|
633
648
|
values[val] = [arg];
|