wikiparser-node 1.0.0-beta.3 → 1.0.1-b

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/README.md +10 -9
  2. package/bundle/bundle.min.js +37 -0
  3. package/config/llwiki.json +1 -35
  4. package/config/minimum.json +136 -0
  5. package/config/moegirl.json +1 -44
  6. package/config/zhwiki.json +1 -466
  7. package/extensions/dist/base.js +105 -0
  8. package/extensions/dist/editor.js +172 -0
  9. package/extensions/dist/highlight.js +46 -0
  10. package/extensions/dist/lint.js +43 -0
  11. package/extensions/editor.css +63 -0
  12. package/extensions/ui.css +119 -0
  13. package/package.json +35 -20
  14. package/config/.schema.json +0 -160
  15. package/dist/index.d.ts +0 -62
  16. package/dist/index.js +0 -305
  17. package/dist/internal.d.ts +0 -44
  18. package/dist/lib/element.d.ts +0 -186
  19. package/dist/lib/element.js +0 -659
  20. package/dist/lib/node.d.ts +0 -178
  21. package/dist/lib/node.js +0 -458
  22. package/dist/lib/range.d.ts +0 -132
  23. package/dist/lib/range.js +0 -387
  24. package/dist/lib/ranges.d.ts +0 -28
  25. package/dist/lib/ranges.js +0 -125
  26. package/dist/lib/text.d.ts +0 -78
  27. package/dist/lib/text.js +0 -227
  28. package/dist/lib/title.d.ts +0 -33
  29. package/dist/lib/title.js +0 -110
  30. package/dist/mixin/attributesParent.js +0 -93
  31. package/dist/mixin/fixed.js +0 -32
  32. package/dist/mixin/hidden.js +0 -23
  33. package/dist/mixin/singleLine.js +0 -26
  34. package/dist/mixin/sol.js +0 -44
  35. package/dist/parser/braces.js +0 -125
  36. package/dist/parser/commentAndExt.js +0 -58
  37. package/dist/parser/converter.js +0 -38
  38. package/dist/parser/externalLinks.js +0 -28
  39. package/dist/parser/hrAndDoubleUnderscore.js +0 -40
  40. package/dist/parser/html.js +0 -38
  41. package/dist/parser/links.js +0 -94
  42. package/dist/parser/list.js +0 -61
  43. package/dist/parser/magicLinks.js +0 -39
  44. package/dist/parser/quotes.js +0 -67
  45. package/dist/parser/selector.js +0 -165
  46. package/dist/parser/table.js +0 -117
  47. package/dist/src/arg.d.ts +0 -77
  48. package/dist/src/arg.js +0 -199
  49. package/dist/src/atom.d.ts +0 -11
  50. package/dist/src/atom.js +0 -24
  51. package/dist/src/attribute.d.ts +0 -106
  52. package/dist/src/attribute.js +0 -436
  53. package/dist/src/attributes.d.ts +0 -119
  54. package/dist/src/attributes.js +0 -366
  55. package/dist/src/converter.d.ts +0 -75
  56. package/dist/src/converter.js +0 -141
  57. package/dist/src/converterFlags.d.ts +0 -107
  58. package/dist/src/converterFlags.js +0 -238
  59. package/dist/src/converterRule.d.ts +0 -96
  60. package/dist/src/converterRule.js +0 -255
  61. package/dist/src/extLink.d.ts +0 -63
  62. package/dist/src/extLink.js +0 -157
  63. package/dist/src/gallery.d.ts +0 -70
  64. package/dist/src/gallery.js +0 -137
  65. package/dist/src/heading.d.ts +0 -82
  66. package/dist/src/heading.js +0 -135
  67. package/dist/src/hidden.d.ts +0 -19
  68. package/dist/src/hidden.js +0 -26
  69. package/dist/src/html.d.ts +0 -113
  70. package/dist/src/html.js +0 -245
  71. package/dist/src/imageParameter.d.ts +0 -82
  72. package/dist/src/imageParameter.js +0 -256
  73. package/dist/src/imagemap.d.ts +0 -77
  74. package/dist/src/imagemap.js +0 -169
  75. package/dist/src/imagemapLink.d.ts +0 -48
  76. package/dist/src/imagemapLink.js +0 -43
  77. package/dist/src/index.d.ts +0 -156
  78. package/dist/src/index.js +0 -819
  79. package/dist/src/link/base.d.ts +0 -69
  80. package/dist/src/link/base.js +0 -231
  81. package/dist/src/link/category.d.ts +0 -14
  82. package/dist/src/link/category.js +0 -28
  83. package/dist/src/link/file.d.ts +0 -105
  84. package/dist/src/link/file.js +0 -269
  85. package/dist/src/link/galleryImage.d.ts +0 -37
  86. package/dist/src/link/galleryImage.js +0 -100
  87. package/dist/src/link/index.d.ts +0 -50
  88. package/dist/src/link/index.js +0 -134
  89. package/dist/src/magicLink.d.ts +0 -46
  90. package/dist/src/magicLink.js +0 -131
  91. package/dist/src/nested.d.ts +0 -47
  92. package/dist/src/nested.js +0 -92
  93. package/dist/src/nowiki/base.d.ts +0 -38
  94. package/dist/src/nowiki/base.js +0 -31
  95. package/dist/src/nowiki/comment.d.ts +0 -42
  96. package/dist/src/nowiki/comment.js +0 -64
  97. package/dist/src/nowiki/dd.d.ts +0 -22
  98. package/dist/src/nowiki/dd.js +0 -46
  99. package/dist/src/nowiki/doubleUnderscore.d.ts +0 -35
  100. package/dist/src/nowiki/doubleUnderscore.js +0 -48
  101. package/dist/src/nowiki/hr.d.ts +0 -30
  102. package/dist/src/nowiki/hr.js +0 -36
  103. package/dist/src/nowiki/index.d.ts +0 -19
  104. package/dist/src/nowiki/index.js +0 -23
  105. package/dist/src/nowiki/list.d.ts +0 -16
  106. package/dist/src/nowiki/list.js +0 -13
  107. package/dist/src/nowiki/noinclude.d.ts +0 -21
  108. package/dist/src/nowiki/noinclude.js +0 -24
  109. package/dist/src/nowiki/quote.d.ts +0 -27
  110. package/dist/src/nowiki/quote.js +0 -58
  111. package/dist/src/onlyinclude.d.ts +0 -38
  112. package/dist/src/onlyinclude.js +0 -66
  113. package/dist/src/paramTag/index.d.ts +0 -50
  114. package/dist/src/paramTag/index.js +0 -78
  115. package/dist/src/paramTag/inputbox.d.ts +0 -7
  116. package/dist/src/paramTag/inputbox.js +0 -21
  117. package/dist/src/parameter.d.ts +0 -95
  118. package/dist/src/parameter.js +0 -204
  119. package/dist/src/pre.d.ts +0 -27
  120. package/dist/src/pre.js +0 -44
  121. package/dist/src/syntax.d.ts +0 -22
  122. package/dist/src/syntax.js +0 -67
  123. package/dist/src/table/base.d.ts +0 -60
  124. package/dist/src/table/base.js +0 -80
  125. package/dist/src/table/index.d.ts +0 -247
  126. package/dist/src/table/index.js +0 -783
  127. package/dist/src/table/td.d.ts +0 -119
  128. package/dist/src/table/td.js +0 -268
  129. package/dist/src/table/tr.d.ts +0 -31
  130. package/dist/src/table/tr.js +0 -50
  131. package/dist/src/table/trBase.d.ts +0 -62
  132. package/dist/src/table/trBase.js +0 -157
  133. package/dist/src/tagPair/ext.d.ts +0 -54
  134. package/dist/src/tagPair/ext.js +0 -150
  135. package/dist/src/tagPair/include.d.ts +0 -43
  136. package/dist/src/tagPair/include.js +0 -43
  137. package/dist/src/tagPair/index.d.ts +0 -57
  138. package/dist/src/tagPair/index.js +0 -113
  139. package/dist/src/transclude.d.ts +0 -200
  140. package/dist/src/transclude.js +0 -732
  141. package/dist/util/debug.js +0 -42
  142. package/dist/util/diff.js +0 -71
  143. package/dist/util/lint.js +0 -38
  144. package/dist/util/string.js +0 -74
  145. package/errors/README +0 -1
  146. package/printed/README +0 -1
@@ -1,732 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TranscludeToken = void 0;
4
- const string_1 = require("../util/string");
5
- const lint_1 = require("../util/lint");
6
- const index_1 = require("../index");
7
- const _1 = require(".");
8
- const parameter_1 = require("./parameter");
9
- const atom_1 = require("./atom");
10
- const syntax_1 = require("./syntax");
11
- /**
12
- * 模板或魔术字
13
- * @classdesc `{childNodes: [AtomToken|SyntaxToken, ...AtomToken, ...ParameterToken]}`
14
- */
15
- class TranscludeToken extends _1.Token {
16
- /** @browser */
17
- type = 'template';
18
- /** @browser */
19
- modifier = '';
20
- /** @browser */
21
- #fragment;
22
- /** @browser */
23
- #valid = true;
24
- /** @browser */
25
- #raw = false;
26
- #args = new Map();
27
- #keys = new Set();
28
- /** 是否存在重复参数 */
29
- get duplication() {
30
- return this.isTemplate() && Boolean(this.hasDuplicatedArgs());
31
- }
32
- /**
33
- * @browser
34
- * @param title 模板标题或魔术字
35
- * @param parts 参数各部分
36
- * @throws `SyntaxError` 非法的模板名称
37
- */
38
- constructor(title, parts, config = index_1.default.getConfig(), accum = []) {
39
- super(undefined, config, true, accum, {
40
- AtomToken: 0, SyntaxToken: 0, ParameterToken: '1:',
41
- });
42
- this.seal('modifier');
43
- const { parserFunction: [insensitive, sensitive] } = config, argSubst = /^(?:\s|\0\d+c\x7F)*\0\d+s\x7F/u.exec(title)?.[0];
44
- if (argSubst) {
45
- this.setAttribute('modifier', argSubst);
46
- title = title.slice(argSubst.length);
47
- }
48
- else if (title.includes(':')) {
49
- const [modifier, ...arg] = title.split(':'), [mt] = /^(?:\s|\0\d+c\x7F)*/u.exec(arg[0] ?? '');
50
- if (this.setModifier(`${modifier}:${mt}`)) {
51
- title = arg.join(':').slice(mt.length);
52
- }
53
- }
54
- if (title.includes(':') || parts.length === 0 && !this.#raw) {
55
- const [magicWord, ...arg] = title.split(':'), cleaned = (0, string_1.removeComment)(magicWord), name = cleaned[arg.length > 0 ? 'trimStart' : 'trim'](), isSensitive = sensitive.includes(name), canonicalCame = insensitive[name.toLowerCase()];
56
- if (isSensitive || canonicalCame) {
57
- this.setAttribute('name', canonicalCame ?? name.toLowerCase()).type = 'magic-word';
58
- const pattern = new RegExp(`^\\s*${name}\\s*$`, isSensitive ? 'u' : 'iu'), token = new syntax_1.SyntaxToken(magicWord, pattern, 'magic-word-name', config, accum, {
59
- 'Stage-1': ':', '!ExtToken': '',
60
- });
61
- super.insertAt(token);
62
- if (arg.length > 0) {
63
- parts.unshift([arg.join(':')]);
64
- }
65
- if (this.name === 'invoke') {
66
- this.setAttribute('acceptable', { SyntaxToken: 0, AtomToken: '1:3', ParameterToken: '3:' });
67
- for (let i = 0; i < 2; i++) {
68
- const part = parts.shift();
69
- if (!part) {
70
- break;
71
- }
72
- const invoke = new atom_1.AtomToken(part.join('='), `invoke-${i ? 'function' : 'module'}`, config, accum, {
73
- 'Stage-1': ':', '!ExtToken': '',
74
- });
75
- super.insertAt(invoke);
76
- }
77
- this.protectChildren('1:3');
78
- }
79
- }
80
- }
81
- if (this.type === 'template') {
82
- const name = (0, string_1.removeComment)((0, string_1.decodeHtml)(title)).split('#')[0].trim();
83
- if (!name || /\0\d+[eh!+-]\x7F|[<>[\]{}\n]|%[\da-f]{2}/u.test(name)) {
84
- accum.pop();
85
- throw new SyntaxError(`非法的模板名称:${(0, string_1.noWrap)(name)}`);
86
- }
87
- const token = new atom_1.AtomToken(title, 'template-name', config, accum, {
88
- 'Stage-2': ':', '!HeadingToken': '',
89
- });
90
- super.insertAt(token);
91
- }
92
- const templateLike = this.isTemplate();
93
- let i = 1;
94
- for (let j = 0; j < parts.length; j++) {
95
- const part = parts[j];
96
- if (!templateLike && !(this.name === 'switch' && j > 0)) {
97
- part[0] = part.join('=');
98
- part.length = 1;
99
- }
100
- if (part.length === 1) {
101
- part.unshift(i);
102
- i++;
103
- }
104
- // @ts-expect-error abstract class
105
- this.insertAt(new parameter_1.ParameterToken(...part, config, accum));
106
- }
107
- this.protectChildren(0);
108
- }
109
- /**
110
- * 设置引用修饰符
111
- * @browser
112
- * @param modifier 引用修饰符
113
- */
114
- setModifier(modifier = '') {
115
- const { parserFunction: [, , raw, subst] } = this.getAttribute('config'), lcModifier = (0, string_1.removeComment)(modifier).trim();
116
- if (modifier && !lcModifier.endsWith(':')) {
117
- return false;
118
- }
119
- const magicWord = lcModifier.slice(0, -1).toLowerCase(), isRaw = raw.includes(magicWord), isSubst = subst.includes(magicWord);
120
- if (this.#raw && isRaw || !this.#raw && (isSubst || modifier === '')
121
- || (index_1.default.running || this.length > 1) && (isRaw || isSubst || modifier === '')) {
122
- this.setAttribute('modifier', modifier);
123
- this.#raw = isRaw;
124
- return Boolean(modifier);
125
- }
126
- return false;
127
- }
128
- /**
129
- * 是否是模板
130
- * @browser
131
- */
132
- isTemplate() {
133
- return this.type === 'template' || this.name === 'invoke';
134
- }
135
- /** @private */
136
- afterBuild() {
137
- if (this.modifier.includes('\0')) {
138
- this.setAttribute('modifier', this.buildFromStr(this.modifier, 'string'));
139
- }
140
- if (this.isTemplate()) {
141
- const isTemplate = this.type === 'template', titleObj = this.normalizeTitle(this.childNodes[isTemplate ? 0 : 1].text(), isTemplate ? 10 : 828);
142
- this.setAttribute(isTemplate ? 'name' : 'module', titleObj.title);
143
- this.#fragment = titleObj.fragment;
144
- this.#valid = titleObj.valid;
145
- /**
146
- * 当事件bubble到`parameter`时,将`oldKey`和`newKey`保存进AstEventData。
147
- * 当继续bubble到`template`时,处理并删除`oldKey`和`newKey`。
148
- * @implements
149
- */
150
- const transcludeListener = (e, data) => {
151
- const { prevTarget } = e, { oldKey, newKey } = data;
152
- if (typeof oldKey === 'string') {
153
- delete data.oldKey;
154
- delete data.newKey;
155
- }
156
- if (prevTarget === this.firstChild && isTemplate
157
- || prevTarget === this.childNodes[1] && !isTemplate && this.name === 'invoke') {
158
- const name = prevTarget.text(), { title, fragment, valid } = this.normalizeTitle(name, 10);
159
- this.setAttribute(isTemplate ? 'name' : 'module', title);
160
- this.#fragment = fragment;
161
- this.#valid = valid;
162
- }
163
- else if (oldKey !== newKey && prevTarget instanceof parameter_1.ParameterToken) {
164
- const oldArgs = this.getArgs(oldKey, false, false);
165
- oldArgs.delete(prevTarget);
166
- this.getArgs(newKey, false, false).add(prevTarget);
167
- this.#keys.add(newKey);
168
- if (oldArgs.size === 0) {
169
- this.#keys.delete(oldKey);
170
- }
171
- }
172
- };
173
- this.addEventListener(['remove', 'insert', 'replace', 'text'], transcludeListener);
174
- }
175
- }
176
- /**
177
- * @override
178
- * @browser
179
- */
180
- toString(selector) {
181
- if (selector && this.matches(selector)) {
182
- return '';
183
- }
184
- const { childNodes, length, firstChild, modifier } = this;
185
- return `{{${modifier}${this.type === 'magic-word'
186
- ? `${firstChild.toString(selector)}${length === 1 ? '' : ':'}${childNodes.slice(1).map(child => child.toString(selector)).join('|')}`
187
- : super.toString(selector, '|')}}}`;
188
- }
189
- /**
190
- * @override
191
- * @browser
192
- */
193
- text() {
194
- const { childNodes, length, firstChild, modifier, type, name } = this;
195
- return type === 'magic-word' && name === 'vardefine'
196
- ? ''
197
- : `{{${modifier}${this.type === 'magic-word'
198
- ? `${firstChild.text()}${length === 1 ? '' : ':'}${(0, string_1.text)(childNodes.slice(1), '|')}`
199
- : super.text('|')}}}`;
200
- }
201
- /** @private */
202
- getPadding() {
203
- return this.modifier.length + 2;
204
- }
205
- /** @private */
206
- getGaps(i) {
207
- return i < this.length - 1 ? 1 : 0;
208
- }
209
- /**
210
- * @override
211
- * @browser
212
- */
213
- print() {
214
- const { childNodes, length, firstChild, modifier } = this;
215
- return `<span class="wpb-${this.type}">{{${modifier}${this.type === 'magic-word'
216
- ? `${firstChild.print()}${length === 1 ? '' : ':'}${(0, string_1.print)(childNodes.slice(1), { sep: '|' })}`
217
- : (0, string_1.print)(childNodes, { sep: '|' })}}}</span>`;
218
- }
219
- /**
220
- * @override
221
- * @browser
222
- */
223
- lint(start = this.getAbsoluteIndex()) {
224
- const errors = super.lint(start), { type, childNodes } = this;
225
- let rect;
226
- if (!this.isTemplate()) {
227
- return errors;
228
- }
229
- else if (this.#fragment !== undefined) {
230
- rect = { start, ...this.getRootNode().posFromIndex(start) };
231
- errors.push((0, lint_1.generateForChild)(childNodes[type === 'template' ? 0 : 1], rect, 'useless fragment'));
232
- }
233
- if (!this.#valid) {
234
- rect = { start, ...this.getRootNode().posFromIndex(start) };
235
- errors.push((0, lint_1.generateForChild)(childNodes[1], rect, 'illegal module name'));
236
- }
237
- const duplicatedArgs = this.getDuplicatedArgs();
238
- if (duplicatedArgs.length > 0) {
239
- rect ??= { start, ...this.getRootNode().posFromIndex(start) };
240
- errors.push(...duplicatedArgs.flatMap(([, args]) => args).map(arg => (0, lint_1.generateForChild)(arg, rect, 'duplicated parameter')));
241
- }
242
- return errors;
243
- }
244
- /**
245
- * 处理匿名参数更改
246
- * @browser
247
- * @param addedToken 新增的参数
248
- */
249
- #handleAnonArgChange(addedToken) {
250
- const args = this.getAnonArgs(), added = typeof addedToken !== 'number', maxAnon = String(args.length + (added ? 0 : 1));
251
- if (added) {
252
- this.#keys.add(maxAnon);
253
- }
254
- else if (!this.hasArg(maxAnon, true)) {
255
- this.#keys.delete(maxAnon);
256
- }
257
- for (let i = added ? args.indexOf(addedToken) : addedToken - 1; i < args.length; i++) {
258
- const token = args[i], { name } = token, newName = String(i + 1);
259
- if (name !== newName) {
260
- this.getArgs(newName, false, false).add(token.setAttribute('name', newName));
261
- if (name) {
262
- this.getArgs(name, false, false).delete(token);
263
- }
264
- }
265
- }
266
- }
267
- /**
268
- * @override
269
- * @browser
270
- * @param token 待插入的子节点
271
- * @param i 插入位置
272
- */
273
- insertAt(token, i = this.length) {
274
- super.insertAt(token, i);
275
- if (token.anon) {
276
- this.#handleAnonArgChange(token);
277
- }
278
- else if (token.name) {
279
- this.getArgs(token.name, false, false).add(token);
280
- this.#keys.add(token.name);
281
- }
282
- return token;
283
- }
284
- /**
285
- * 获取所有参数
286
- * @browser
287
- */
288
- getAllArgs() {
289
- return this.childNodes.filter(child => child.type === 'parameter');
290
- }
291
- /**
292
- * 获取匿名参数
293
- * @browser
294
- */
295
- getAnonArgs() {
296
- return this.getAllArgs().filter(({ anon }) => anon);
297
- }
298
- /**
299
- * 获取指定参数
300
- * @browser
301
- * @param key 参数名
302
- * @param exact 是否匹配匿名性
303
- * @param copy 是否返回一个备份
304
- */
305
- getArgs(key, exact = false, copy = true) {
306
- const keyStr = String(key).replace(/^[ \t\n\0\v]+|(?<=[^ \t\n\0\v])[ \t\n\0\v]+$/gu, '');
307
- let args;
308
- if (this.#args.has(keyStr)) {
309
- args = this.#args.get(keyStr);
310
- }
311
- else {
312
- args = new Set(this.getAllArgs().filter(({ name }) => keyStr === name));
313
- this.#args.set(keyStr, args);
314
- }
315
- // @ts-expect-error isNaN
316
- if (exact && !isNaN(keyStr)) {
317
- args = new Set([...args].filter(({ anon }) => typeof key === 'number' === anon));
318
- }
319
- else if (copy) {
320
- args = new Set(args);
321
- }
322
- return args;
323
- }
324
- /**
325
- * 获取重名参数
326
- * @browser
327
- * @throws `Error` 仅用于模板
328
- */
329
- getDuplicatedArgs() {
330
- if (this.isTemplate()) {
331
- return [...this.#args].filter(([, { size }]) => size > 1).map(([key, args]) => [key, [...args]]);
332
- }
333
- throw new Error(`${this.constructor.name}.getDuplicatedArgs 方法仅供模板使用!`);
334
- }
335
- /**
336
- * 对特定魔术字获取可能的取值
337
- * @browser
338
- * @throws `Error` 不是可接受的魔术字
339
- */
340
- getPossibleValues() {
341
- const { type, name, childNodes, constructor: { name: cName } } = this;
342
- if (type === 'template') {
343
- throw new Error(`${cName}.getPossibleValues 方法仅供特定魔术字使用!`);
344
- }
345
- let start;
346
- switch (name) {
347
- case 'if':
348
- case 'ifexist':
349
- case 'ifexpr':
350
- case 'iferror':
351
- start = 2;
352
- break;
353
- case 'ifeq':
354
- start = 3;
355
- break;
356
- default:
357
- throw new Error(`${cName}.getPossibleValues 方法仅供特定魔术字使用!`);
358
- }
359
- const queue = childNodes.slice(start, start + 2).map(({ childNodes: [, value] }) => value);
360
- for (let i = 0; i < queue.length;) {
361
- const { length, 0: first } = queue[i].childNodes.filter(child => child.text().trim());
362
- if (length === 0) {
363
- queue.splice(i, 1);
364
- }
365
- else if (length > 1 || first.type !== 'magic-word') {
366
- i++;
367
- }
368
- else {
369
- try {
370
- const possibleValues = first.getPossibleValues();
371
- queue.splice(i, 1, ...possibleValues);
372
- i += possibleValues.length;
373
- }
374
- catch {
375
- i++;
376
- }
377
- }
378
- }
379
- return queue;
380
- }
381
- /** @override */
382
- cloneNode() {
383
- const [first, ...cloned] = this.cloneChildNodes(), config = this.getAttribute('config');
384
- return index_1.default.run(() => {
385
- // @ts-expect-error abstract class
386
- const token = new TranscludeToken(this.type === 'template' ? '' : first.text(), [], config);
387
- if (this.#raw) {
388
- token.setModifier(this.modifier);
389
- }
390
- else {
391
- token.setAttribute('modifier', this.modifier);
392
- }
393
- token.firstChild.safeReplaceWith(first);
394
- token.afterBuild();
395
- token.append(...cloned);
396
- return token;
397
- });
398
- }
399
- /** 替换引用 */
400
- subst() {
401
- this.setModifier('subst:');
402
- }
403
- /** 安全的替换引用 */
404
- safesubst() {
405
- this.setModifier('safesubst:');
406
- }
407
- /** @private */
408
- hasAttribute(key) {
409
- return key === 'keys' || super.hasAttribute(key);
410
- }
411
- /** @private */
412
- getAttribute(key) {
413
- if (key === 'args') {
414
- return new Map(this.#args);
415
- }
416
- else if (key === 'keys') {
417
- return this.#keys;
418
- }
419
- return super.getAttribute(key);
420
- }
421
- /**
422
- * @override
423
- * @param i 移除位置
424
- */
425
- removeAt(i) {
426
- const token = super.removeAt(i);
427
- if (token.anon) {
428
- this.#handleAnonArgChange(Number(token.name));
429
- }
430
- else {
431
- const args = this.getArgs(token.name, false, false);
432
- args.delete(token);
433
- if (args.size === 0) {
434
- this.#keys.delete(token.name);
435
- }
436
- }
437
- return token;
438
- }
439
- /**
440
- * 是否具有某参数
441
- * @param key 参数名
442
- * @param exact 是否匹配匿名性
443
- */
444
- hasArg(key, exact = false) {
445
- return this.getArgs(key, exact, false).size > 0;
446
- }
447
- /**
448
- * 获取生效的指定参数
449
- * @param key 参数名
450
- * @param exact 是否匹配匿名性
451
- */
452
- getArg(key, exact = false) {
453
- return [...this.getArgs(key, exact, false)].sort((a, b) => a.compareDocumentPosition(b)).at(-1);
454
- }
455
- /**
456
- * 移除指定参数
457
- * @param key 参数名
458
- * @param exact 是否匹配匿名性
459
- */
460
- removeArg(key, exact = false) {
461
- index_1.default.run(() => {
462
- for (const token of this.getArgs(key, exact, false)) {
463
- this.removeChild(token);
464
- }
465
- });
466
- }
467
- /** 获取所有参数名 */
468
- getKeys() {
469
- const args = this.getAllArgs();
470
- if (this.#keys.size === 0 && args.length > 0) {
471
- for (const { name } of args) {
472
- this.#keys.add(name);
473
- }
474
- }
475
- return [...this.#keys];
476
- }
477
- /**
478
- * 获取参数值
479
- * @param key 参数名
480
- */
481
- getValues(key) {
482
- return [...this.getArgs(key, false, false)].map(token => token.getValue());
483
- }
484
- /**
485
- * 获取生效的参数值
486
- * @param key 参数名
487
- */
488
- getValue(key) {
489
- return key === undefined
490
- ? Object.fromEntries(this.getKeys().map(k => [k, this.getValue(k)]))
491
- : this.getArg(key)?.getValue();
492
- }
493
- /**
494
- * 插入匿名参数
495
- * @param val 参数值
496
- * @throws `SyntaxError` 非法的匿名参数
497
- */
498
- newAnonArg(val) {
499
- const templateLike = this.isTemplate(), wikitext = `{{${templateLike ? ':T|' : 'lc:'}${val}}}`, root = index_1.default.parse(wikitext, this.getAttribute('include'), 2, this.getAttribute('config')), { length, firstChild: transclude } = root, targetType = templateLike ? 'template' : 'magic-word';
500
- if (length !== 1 || transclude.type !== targetType || transclude.length !== 2) {
501
- throw new SyntaxError(`非法的匿名参数:${(0, string_1.noWrap)(val)}`);
502
- }
503
- const { name, lastChild } = transclude, targetName = templateLike ? 'T' : 'lc';
504
- if (name === targetName && lastChild.anon) {
505
- return this.insertAt(lastChild);
506
- }
507
- throw new SyntaxError(`非法的匿名参数:${(0, string_1.noWrap)(val)}`);
508
- }
509
- /**
510
- * 设置参数值
511
- * @param key 参数名
512
- * @param value 参数值
513
- * @throws `Error` 仅用于模板
514
- * @throws `SyntaxError` 非法的命名参数
515
- */
516
- setValue(key, value) {
517
- if (!this.isTemplate()) {
518
- throw new Error(`${this.constructor.name}.setValue 方法仅供模板使用!`);
519
- }
520
- const token = this.getArg(key);
521
- if (token) {
522
- token.setValue(value);
523
- return;
524
- }
525
- const wikitext = `{{:T|${key}=${value}}}`, root = index_1.default.parse(wikitext, this.getAttribute('include'), 2, this.getAttribute('config')), { length, firstChild: template } = root;
526
- if (length !== 1 || template.type !== 'template' || template.length !== 2) {
527
- throw new SyntaxError(`非法的命名参数:${key}=${(0, string_1.noWrap)(value)}`);
528
- }
529
- const { name, lastChild: parameter } = template;
530
- if (name !== 'T' || parameter.name !== key) {
531
- throw new SyntaxError(`非法的命名参数:${key}=${(0, string_1.noWrap)(value)}`);
532
- }
533
- this.insertAt(parameter);
534
- }
535
- /**
536
- * 将匿名参数改写为命名参数
537
- * @throws `Error` 仅用于模板
538
- */
539
- anonToNamed() {
540
- if (!this.isTemplate()) {
541
- throw new Error(`${this.constructor.name}.anonToNamed 方法仅供模板使用!`);
542
- }
543
- for (const token of this.getAnonArgs()) {
544
- token.firstChild.replaceChildren(token.name);
545
- }
546
- }
547
- /**
548
- * 替换模板名
549
- * @param title 模板名
550
- * @throws `Error` 仅用于模板
551
- * @throws `SyntaxError` 非法的模板名称
552
- */
553
- replaceTemplate(title) {
554
- if (this.type === 'magic-word') {
555
- throw new Error(`${this.constructor.name}.replaceTemplate 方法仅用于更换模板!`);
556
- }
557
- const root = index_1.default.parse(`{{${title}}}`, this.getAttribute('include'), 2, this.getAttribute('config')), { length, firstChild: template } = root;
558
- if (length !== 1 || template.type !== 'template' || template.length !== 1) {
559
- throw new SyntaxError(`非法的模板名称:${title}`);
560
- }
561
- this.firstChild.replaceChildren(...template.firstChild.childNodes);
562
- }
563
- /**
564
- * 替换模块名
565
- * @param title 模块名
566
- * @throws `Error` 仅用于模块
567
- * @throws `SyntaxError` 非法的模块名称
568
- */
569
- replaceModule(title) {
570
- if (this.type !== 'magic-word' || this.name !== 'invoke') {
571
- throw new Error(`${this.constructor.name}.replaceModule 方法仅用于更换模块!`);
572
- }
573
- const root = index_1.default.parse(`{{#invoke:${title}}}`, this.getAttribute('include'), 2, this.getAttribute('config')), { length, firstChild: invoke } = root, { type, name, lastChild } = invoke;
574
- if (length !== 1 || type !== 'magic-word' || name !== 'invoke' || invoke.length !== 2) {
575
- throw new SyntaxError(`非法的模块名称:${title}`);
576
- }
577
- else if (this.length > 1) {
578
- this.childNodes[1].replaceChildren(...lastChild.childNodes);
579
- }
580
- else {
581
- invoke.destroy();
582
- super.insertAt(lastChild);
583
- }
584
- }
585
- /**
586
- * 替换模块函数
587
- * @param func 模块函数名
588
- * @throws `Error` 仅用于模块
589
- * @throws `Error` 尚未指定模块名称
590
- * @throws `SyntaxError` 非法的模块函数名
591
- */
592
- replaceFunction(func) {
593
- if (this.type !== 'magic-word' || this.name !== 'invoke') {
594
- throw new Error(`${this.constructor.name}.replaceModule 方法仅用于更换模块!`);
595
- }
596
- else if (this.length < 2) {
597
- throw new Error('尚未指定模块名称!');
598
- }
599
- const root = index_1.default.parse(`{{#invoke:M|${func}}}`, this.getAttribute('include'), 2, this.getAttribute('config')), { length, firstChild: invoke } = root, { type, name, lastChild } = invoke;
600
- if (length !== 1 || type !== 'magic-word' || name !== 'invoke' || invoke.length !== 3) {
601
- throw new SyntaxError(`非法的模块函数名:${func}`);
602
- }
603
- else if (this.length > 2) {
604
- this.childNodes[2].replaceChildren(...lastChild.childNodes);
605
- }
606
- else {
607
- invoke.destroy();
608
- super.insertAt(lastChild);
609
- }
610
- }
611
- /**
612
- * 是否存在重名参数
613
- * @throws `Error` 仅用于模板
614
- */
615
- hasDuplicatedArgs() {
616
- if (this.isTemplate()) {
617
- return this.getAllArgs().length - this.getKeys().length;
618
- }
619
- throw new Error(`${this.constructor.name}.hasDuplicatedArgs 方法仅供模板使用!`);
620
- }
621
- /**
622
- * 修复重名参数:
623
- * `aggressive = false`时只移除空参数和全同参数,优先保留匿名参数,否则将所有匿名参数更改为命名。
624
- * `aggressive = true`时还会尝试处理连续的以数字编号的参数。
625
- * @param aggressive 是否使用有更大风险的修复手段
626
- */
627
- fixDuplication(aggressive = false) {
628
- if (!this.hasDuplicatedArgs()) {
629
- return [];
630
- }
631
- const duplicatedKeys = [];
632
- let { length: anonCount } = this.getAnonArgs();
633
- for (const [key, args] of this.getDuplicatedArgs()) {
634
- if (args.length <= 1) {
635
- continue;
636
- }
637
- const values = new Map();
638
- for (const arg of args) {
639
- const val = arg.getValue().trim();
640
- if (values.has(val)) {
641
- values.get(val).push(arg);
642
- }
643
- else {
644
- values.set(val, [arg]);
645
- }
646
- }
647
- // @ts-expect-error isNaN
648
- let noMoreAnon = anonCount === 0 || isNaN(key);
649
- const emptyArgs = values.get('') ?? [], duplicatedArgs = [...values].filter(([val, { length }]) => val && length > 1).flatMap(([, curArgs]) => {
650
- const anonIndex = noMoreAnon ? -1 : curArgs.findIndex(({ anon }) => anon);
651
- if (anonIndex !== -1) {
652
- noMoreAnon = true;
653
- }
654
- curArgs.splice(anonIndex, 1);
655
- return curArgs;
656
- }), badArgs = [...emptyArgs, ...duplicatedArgs], index = noMoreAnon ? -1 : emptyArgs.findIndex(({ anon }) => anon);
657
- if (badArgs.length === args.length) {
658
- badArgs.splice(index, 1);
659
- }
660
- else if (index !== -1) {
661
- this.anonToNamed();
662
- anonCount = 0;
663
- }
664
- for (const arg of badArgs) {
665
- arg.remove();
666
- }
667
- let remaining = args.length - badArgs.length;
668
- if (remaining === 1) {
669
- continue;
670
- }
671
- else if (aggressive && (anonCount ? /\D\d+$/u : /(?:^|\D)\d+$/u).test(key)) {
672
- let last;
673
- const str = key.slice(0, -/(?<!\d)\d+$/u.exec(key)[0].length), regex = new RegExp(`^${(0, string_1.escapeRegExp)(str)}\\d+$`, 'u'), series = this.getAllArgs().filter(({ name }) => regex.test(name)), ordered = series.every(({ name }, i) => {
674
- const j = Number(name.slice(str.length)), cmp = j <= i + 1 && (i === 0 || j >= last || name === key);
675
- last = j;
676
- return cmp;
677
- });
678
- if (ordered) {
679
- for (let i = 0; i < series.length; i++) {
680
- const name = `${str}${i + 1}`, arg = series[i];
681
- if (arg.name !== name) {
682
- if (arg.name === key) {
683
- remaining--;
684
- }
685
- arg.rename(name, true);
686
- }
687
- }
688
- }
689
- }
690
- if (remaining > 1) {
691
- index_1.default.error(`${this.type === 'template'
692
- ? this.name
693
- : this.normalizeTitle(this.childNodes[1]?.text() ?? '', 828).title} 还留有 ${remaining} 个重复的 ${key} 参数:${[...this.getArgs(key)].map(arg => {
694
- const { top, left } = arg.getBoundingClientRect();
695
- return `第 ${String(top)} 行第 ${String(left)} 列`;
696
- }).join('、')}`);
697
- duplicatedKeys.push(key);
698
- continue;
699
- }
700
- }
701
- return duplicatedKeys;
702
- }
703
- /**
704
- * 转义模板内的表格
705
- * @throws `Error` 转义失败
706
- */
707
- escapeTables() {
708
- const count = this.hasDuplicatedArgs();
709
- if (!/\n[^\S\n]*(?::+\s*)?\{\|[^\n]*\n\s*(?:\S[^\n]*\n\s*)*\|\}/u.test(this.text()) || !count) {
710
- return this;
711
- }
712
- const stripped = String(this).slice(2, -2), include = this.getAttribute('include'), config = this.getAttribute('config'), parsed = index_1.default.parse(stripped, include, 4, config);
713
- for (const table of parsed.childNodes) {
714
- if (table.type === 'table') {
715
- table.escape();
716
- }
717
- }
718
- const { firstChild, length } = index_1.default.parse(`{{${String(parsed)}}}`, include, 2, config);
719
- if (length !== 1 || !(firstChild instanceof TranscludeToken)) {
720
- throw new Error('转义表格失败!');
721
- }
722
- const newCount = firstChild.hasDuplicatedArgs();
723
- if (newCount === count) {
724
- return this;
725
- }
726
- index_1.default.info(`共修复了 ${count - newCount} 个重复参数。`);
727
- this.safeReplaceWith(firstChild);
728
- return firstChild;
729
- }
730
- }
731
- exports.TranscludeToken = TranscludeToken;
732
- index_1.default.classes['TranscludeToken'] = __filename;