wikiparser-node 1.18.2 → 1.18.3
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 +6 -1
- package/bundle/bundle-es7.min.js +29 -29
- package/bundle/bundle-lsp.min.js +30 -30
- package/bundle/bundle.min.js +26 -26
- package/config/minimum.json +7 -0
- package/dist/addon/token.js +10 -9
- package/dist/addon/transclude.js +1 -3
- package/dist/base.d.mts +4 -0
- package/dist/base.d.ts +4 -0
- package/dist/base.js +1 -0
- package/dist/base.mjs +2 -1
- package/dist/bin/config.js +3 -2
- package/dist/index.d.ts +5 -1
- package/dist/index.js +65 -72
- package/dist/lib/element.d.ts +6 -0
- package/dist/lib/element.js +543 -466
- package/dist/lib/lsp.d.ts +1 -0
- package/dist/lib/lsp.js +16 -10
- package/dist/lib/redirectMap.d.ts +7 -0
- package/dist/lib/redirectMap.js +31 -0
- package/dist/lib/text.d.ts +2 -2
- package/dist/lib/text.js +383 -325
- package/dist/lib/title.d.ts +23 -4
- package/dist/lib/title.js +17 -5
- package/dist/mixin/noEscape.d.ts +4 -0
- package/dist/mixin/noEscape.js +22 -0
- package/dist/mixin/readOnly.d.ts +4 -0
- package/dist/mixin/readOnly.js +26 -0
- package/dist/parser/braces.js +29 -15
- package/dist/parser/commentAndExt.js +5 -16
- package/dist/parser/links.js +1 -1
- package/dist/parser/quotes.js +1 -1
- package/dist/parser/redirect.js +1 -3
- package/dist/src/arg.js +253 -202
- package/dist/src/attribute.d.ts +0 -5
- package/dist/src/attribute.js +3 -7
- package/dist/src/converter.js +213 -162
- package/dist/src/gallery.js +1 -2
- package/dist/src/heading.js +5 -6
- package/dist/src/imageParameter.js +16 -16
- package/dist/src/imagemap.js +3 -2
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +722 -694
- package/dist/src/link/base.js +292 -241
- package/dist/src/link/file.js +13 -17
- package/dist/src/link/galleryImage.js +1 -1
- package/dist/src/link/redirectTarget.js +1 -2
- package/dist/src/magicLink.d.ts +0 -6
- package/dist/src/magicLink.js +5 -15
- package/dist/src/nested.js +7 -7
- package/dist/src/nowiki/base.js +2 -1
- package/dist/src/nowiki/index.js +4 -3
- package/dist/src/onlyinclude.js +95 -44
- package/dist/src/parameter.d.ts +0 -6
- package/dist/src/parameter.js +1 -13
- package/dist/src/redirect.js +6 -6
- package/dist/src/syntax.d.ts +4 -1
- package/dist/src/syntax.js +4 -1
- package/dist/src/table/base.d.ts +0 -5
- package/dist/src/table/base.js +2 -9
- package/dist/src/table/index.js +6 -3
- package/dist/src/table/td.d.ts +1 -0
- package/dist/src/table/td.js +2 -3
- package/dist/src/table/trBase.js +3 -1
- package/dist/src/tagPair/index.js +2 -1
- package/dist/src/transclude.js +705 -657
- package/dist/util/debug.js +10 -3
- package/dist/util/string.js +4 -5
- package/extensions/dist/base.js +8 -6
- package/extensions/dist/lint.js +1 -1
- package/extensions/es7/base.js +8 -6
- package/extensions/es7/lint.js +1 -1
- package/extensions/ui.css +1 -1
- package/package.json +3 -3
package/dist/src/transclude.js
CHANGED
|
@@ -1,4 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
3
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
4
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
5
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
6
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
7
|
+
var _, done = false;
|
|
8
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
9
|
+
var context = {};
|
|
10
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
11
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
12
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
13
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
14
|
+
if (kind === "accessor") {
|
|
15
|
+
if (result === void 0) continue;
|
|
16
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
17
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
18
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
19
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
20
|
+
}
|
|
21
|
+
else if (_ = accept(result)) {
|
|
22
|
+
if (kind === "field") initializers.unshift(_);
|
|
23
|
+
else descriptor[key] = _;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
27
|
+
done = true;
|
|
28
|
+
};
|
|
29
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
30
|
+
var useValue = arguments.length > 2;
|
|
31
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
32
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
33
|
+
}
|
|
34
|
+
return useValue ? value : void 0;
|
|
35
|
+
};
|
|
2
36
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
37
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
38
|
};
|
|
@@ -14,6 +48,7 @@ const parameter_1 = require("./parameter");
|
|
|
14
48
|
const atom_1 = require("./atom");
|
|
15
49
|
const syntax_1 = require("./syntax");
|
|
16
50
|
/* NOT FOR BROWSER */
|
|
51
|
+
const noEscape_1 = require("../mixin/noEscape");
|
|
17
52
|
const index_2 = __importDefault(require("../index"));
|
|
18
53
|
const basicMagicWords = new Map([['=', '='], ['!', '|']]);
|
|
19
54
|
/**
|
|
@@ -22,705 +57,718 @@ const basicMagicWords = new Map([['=', '='], ['!', '|']]);
|
|
|
22
57
|
* 模板或魔术字
|
|
23
58
|
* @classdesc `{childNodes: [AtomToken|SyntaxToken, ...AtomToken[], ...ParameterToken[]]}`
|
|
24
59
|
*/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
60
|
+
let TranscludeToken = (() => {
|
|
61
|
+
let _classDecorators = [noEscape_1.noEscape];
|
|
62
|
+
let _classDescriptor;
|
|
63
|
+
let _classExtraInitializers = [];
|
|
64
|
+
let _classThis;
|
|
65
|
+
let _classSuper = index_1.Token;
|
|
66
|
+
var TranscludeToken = class extends _classSuper {
|
|
67
|
+
static { _classThis = this; }
|
|
68
|
+
static {
|
|
69
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
70
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
71
|
+
TranscludeToken = _classThis = _classDescriptor.value;
|
|
72
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
73
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
74
|
+
}
|
|
75
|
+
modifier = '';
|
|
76
|
+
#type = 'template';
|
|
77
|
+
#colon = ':';
|
|
78
|
+
#raw = false;
|
|
79
|
+
#args = new Map();
|
|
80
|
+
#title;
|
|
81
|
+
/* NOT FOR BROWSER */
|
|
82
|
+
#keys = new Set();
|
|
83
|
+
/* NOT FOR BROWSER END */
|
|
84
|
+
get type() {
|
|
85
|
+
return this.#type;
|
|
46
86
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
? sensitive[name]
|
|
83
|
-
: Object.prototype.hasOwnProperty.call(insensitive, lcName) && insensitive[lcName], isFunc = isOldSchema && isSensitive
|
|
84
|
-
|| !('functionHook' in config) || functionHook.includes(canonicalName), isVar = isOldSchema && isSensitive || variable.includes(canonicalName);
|
|
85
|
-
if (isFunction ? canonicalName && isFunc : isVar) {
|
|
86
|
-
this.setAttribute('name', canonicalName || lcName.replace(/^#|:$/u, ''));
|
|
87
|
-
this.#type = 'magic-word';
|
|
88
|
-
if (fullWidth) {
|
|
89
|
-
this.#colon = ':';
|
|
87
|
+
/* NOT FOR BROWSER */
|
|
88
|
+
/** whether to contain duplicated parameters / 是否存在重复参数 */
|
|
89
|
+
get duplication() {
|
|
90
|
+
return this.isTemplate() && Boolean(this.hasDuplicatedArgs());
|
|
91
|
+
}
|
|
92
|
+
set duplication(duplication) {
|
|
93
|
+
if (this.duplication && !duplication) {
|
|
94
|
+
this.fixDuplication();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/* NOT FOR BROWSER END */
|
|
98
|
+
/**
|
|
99
|
+
* @param title 模板标题或魔术字
|
|
100
|
+
* @param parts 参数各部分
|
|
101
|
+
* @throws `SyntaxError` 非法的模板名称
|
|
102
|
+
*/
|
|
103
|
+
constructor(title, parts, config, accum = []) {
|
|
104
|
+
let heading;
|
|
105
|
+
const m = /^(?:\s|\0\d+[cn]\x7F)*\0(\d+)h\x7F(?:\s|\0\d+[cn]\x7F)*/u.exec(title);
|
|
106
|
+
if (m) {
|
|
107
|
+
heading = Number(m[1]);
|
|
108
|
+
title = title.replace(`\0${heading}h\x7F`, accum[heading].toString().replace(/^\n/u, ''));
|
|
109
|
+
}
|
|
110
|
+
super(undefined, config, accum, {
|
|
111
|
+
AtomToken: 0, SyntaxToken: 0, ParameterToken: '1:',
|
|
112
|
+
});
|
|
113
|
+
const { parserFunction: [insensitive, sensitive], variable, functionHook } = config, argSubst = /^(?:\s|\0\d+[cn]\x7F)*\0\d+s\x7F/u.exec(title)?.[0];
|
|
114
|
+
if (argSubst) {
|
|
115
|
+
this.setAttribute('modifier', argSubst);
|
|
116
|
+
title = title.slice(argSubst.length);
|
|
117
|
+
}
|
|
118
|
+
else if (title.includes(':')) {
|
|
119
|
+
const [modifier, ...arg] = title.split(':'), [mt] = /^(?:\s|\0\d+[cn]\x7F)*/u.exec(arg[0] ?? '');
|
|
120
|
+
if (this.setModifier(`${modifier}:${mt}`)) {
|
|
121
|
+
title = arg.join(':').slice(mt.length);
|
|
90
122
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
123
|
+
}
|
|
124
|
+
const colon = title.search(/[::]/u), fullWidth = title[colon] === ':', isFunction = colon !== -1;
|
|
125
|
+
if (isFunction || parts.length === 0 && !this.#raw) {
|
|
126
|
+
const magicWord = isFunction ? title.slice(0, colon) : title, arg = isFunction && title.slice(colon + 1), cleaned = (0, string_1.removeComment)(magicWord), name = isFunction
|
|
127
|
+
? cleaned.slice(cleaned.search(/\S/u)) + (fullWidth ? ':' : '')
|
|
128
|
+
: cleaned.trim(), lcName = name.toLowerCase(), isOldSchema = Array.isArray(sensitive), isSensitive = isOldSchema
|
|
129
|
+
? sensitive.includes(name)
|
|
130
|
+
: Object.prototype.hasOwnProperty.call(sensitive, name), canonicalName = !isOldSchema && isSensitive
|
|
131
|
+
? sensitive[name]
|
|
132
|
+
: Object.prototype.hasOwnProperty.call(insensitive, lcName) && insensitive[lcName], isFunc = isOldSchema && isSensitive
|
|
133
|
+
|| !('functionHook' in config) || functionHook.includes(canonicalName), isVar = isOldSchema && isSensitive || variable.includes(canonicalName);
|
|
134
|
+
if (isFunction ? canonicalName && isFunc : isVar) {
|
|
135
|
+
this.setAttribute('name', canonicalName || lcName.replace(/^#|:$/u, ''));
|
|
136
|
+
this.#type = 'magic-word';
|
|
137
|
+
if (fullWidth) {
|
|
138
|
+
this.#colon = ':';
|
|
139
|
+
}
|
|
140
|
+
/^\s*uc\s*$/iu; // eslint-disable-line @typescript-eslint/no-unused-expressions
|
|
141
|
+
const token = new syntax_1.SyntaxToken(magicWord, new RegExp(String.raw `^\s*${name}\s*$`, isSensitive ? 'u' : 'iu'), 'magic-word-name', config, accum, { 'Stage-1': ':', '!ExtToken': '' });
|
|
142
|
+
super.insertAt(token);
|
|
143
|
+
if (arg !== false) {
|
|
144
|
+
parts.unshift([arg]);
|
|
145
|
+
}
|
|
146
|
+
if (this.name === 'invoke') {
|
|
147
|
+
/* NOT FOR BROWSER */
|
|
148
|
+
this.setAttribute('acceptable', { SyntaxToken: 0, AtomToken: '1:3', ParameterToken: '3:' });
|
|
149
|
+
this.protectChildren('1:3');
|
|
150
|
+
/* NOT FOR BROWSER END */
|
|
151
|
+
for (let i = 0; i < 2; i++) {
|
|
152
|
+
const part = parts.shift();
|
|
153
|
+
if (!part) {
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
const invoke = new atom_1.AtomToken(part.join('='), `invoke-${i ? 'function' : 'module'}`, config, accum, { 'Stage-2': ':', '!ExtToken': '', '!HeadingToken': '' });
|
|
157
|
+
super.insertAt(invoke);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
98
160
|
}
|
|
99
|
-
|
|
161
|
+
}
|
|
162
|
+
if (this.type === 'template') {
|
|
163
|
+
const name = (0, string_1.removeComment)(title).trim();
|
|
164
|
+
if (!this.normalizeTitle(name, 10, { halfParsed: true, temporary: true }).valid) {
|
|
165
|
+
accum.pop();
|
|
100
166
|
/* NOT FOR BROWSER */
|
|
101
|
-
|
|
102
|
-
this.protectChildren('1:3');
|
|
167
|
+
index_2.default.debug(`Invalid template name: ${(0, string_1.noWrap)(name)}`);
|
|
103
168
|
/* NOT FOR BROWSER END */
|
|
104
|
-
|
|
105
|
-
const part = parts.shift();
|
|
106
|
-
if (!part) {
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
const invoke = new atom_1.AtomToken(part.join('='), `invoke-${i ? 'function' : 'module'}`, config, accum, { 'Stage-2': ':', '!ExtToken': '', '!HeadingToken': '' });
|
|
110
|
-
super.insertAt(invoke);
|
|
111
|
-
}
|
|
169
|
+
throw new SyntaxError('Invalid template name');
|
|
112
170
|
}
|
|
171
|
+
const token = new atom_1.AtomToken(title, 'template-name', config, accum, {
|
|
172
|
+
'Stage-2': ':', '!ExtToken': '', '!HeadingToken': '',
|
|
173
|
+
});
|
|
174
|
+
super.insertAt(token);
|
|
113
175
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (!this.normalizeTitle(name, 10, true, true).valid) {
|
|
118
|
-
accum.pop();
|
|
119
|
-
/* NOT FOR BROWSER */
|
|
120
|
-
index_2.default.debug(`Invalid template name: ${(0, string_1.noWrap)(name)}`);
|
|
121
|
-
/* NOT FOR BROWSER END */
|
|
122
|
-
throw new SyntaxError('Invalid template name');
|
|
176
|
+
if (typeof heading === 'number') {
|
|
177
|
+
// @ts-expect-error sparse array
|
|
178
|
+
accum[heading] = undefined;
|
|
123
179
|
}
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
part[0] = part.join('=');
|
|
139
|
-
part.length = 1;
|
|
180
|
+
const templateLike = this.isTemplate();
|
|
181
|
+
let i = 1;
|
|
182
|
+
for (let j = 0; j < parts.length; j++) {
|
|
183
|
+
const part = parts[j];
|
|
184
|
+
if (!(templateLike || this.name === 'switch' && j > 0 || this.name === 'tag' && j > 1)) {
|
|
185
|
+
part[0] = part.join('=');
|
|
186
|
+
part.length = 1;
|
|
187
|
+
}
|
|
188
|
+
if (part.length === 1) {
|
|
189
|
+
part.unshift(i);
|
|
190
|
+
i++;
|
|
191
|
+
}
|
|
192
|
+
// @ts-expect-error abstract class
|
|
193
|
+
this.insertAt(new parameter_1.ParameterToken(...part, config, accum));
|
|
140
194
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
195
|
+
this.seal('modifier');
|
|
196
|
+
/* NOT FOR BROWSER */
|
|
197
|
+
this.protectChildren(0);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Set the transclusion modifier
|
|
201
|
+
*
|
|
202
|
+
* 设置引用修饰符
|
|
203
|
+
* @param modifier transclusion modifier / 引用修饰符
|
|
204
|
+
*/
|
|
205
|
+
setModifier(modifier) {
|
|
206
|
+
const { parserFunction: [, , raw, subst] } = this.getAttribute('config'), lcModifier = (0, string_1.removeComment)(modifier).trim();
|
|
207
|
+
if (modifier && !lcModifier.endsWith(':')) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
const magicWord = lcModifier.slice(0, -1).toLowerCase(), isRaw = raw.includes(magicWord), isSubst = subst.includes(magicWord);
|
|
211
|
+
if (this.#raw && isRaw
|
|
212
|
+
|| !this.#raw && (isSubst || modifier === '')
|
|
213
|
+
|| (debug_1.Shadow.running || this.length > 1) && (isRaw || isSubst || modifier === '')) {
|
|
214
|
+
this.setAttribute('modifier', modifier);
|
|
215
|
+
this.#raw = isRaw;
|
|
216
|
+
return Boolean(modifier);
|
|
144
217
|
}
|
|
145
|
-
// @ts-expect-error abstract class
|
|
146
|
-
this.insertAt(new parameter_1.ParameterToken(...part, config, accum));
|
|
147
|
-
}
|
|
148
|
-
this.seal('modifier');
|
|
149
|
-
/* NOT FOR BROWSER */
|
|
150
|
-
this.protectChildren(0);
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Set the transclusion modifier
|
|
154
|
-
*
|
|
155
|
-
* 设置引用修饰符
|
|
156
|
-
* @param modifier transclusion modifier / 引用修饰符
|
|
157
|
-
*/
|
|
158
|
-
setModifier(modifier) {
|
|
159
|
-
const { parserFunction: [, , raw, subst] } = this.getAttribute('config'), lcModifier = (0, string_1.removeComment)(modifier).trim();
|
|
160
|
-
if (modifier && !lcModifier.endsWith(':')) {
|
|
161
218
|
return false;
|
|
162
219
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
/* istanbul ignore if */
|
|
196
|
-
if (this.type !== 'magic-word' || this.name !== 'invoke') {
|
|
197
|
-
throw new Error('TranscludeToken.getModule method is only for modules!');
|
|
198
|
-
}
|
|
199
|
-
return [
|
|
200
|
-
this.#getTitle().title,
|
|
201
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
202
|
-
this.childNodes[2]?.toString(true).trim(),
|
|
203
|
-
];
|
|
220
|
+
/**
|
|
221
|
+
* Check if it is a template or a module
|
|
222
|
+
*
|
|
223
|
+
* 是否是模板或模块
|
|
224
|
+
*/
|
|
225
|
+
isTemplate() {
|
|
226
|
+
return this.type === 'template' || this.name === 'invoke';
|
|
227
|
+
}
|
|
228
|
+
/** 获取模板或模块名 */
|
|
229
|
+
#getTitle() {
|
|
230
|
+
const isTemplate = this.type === 'template', title = this.normalizeTitle(this.childNodes[isTemplate ? 0 : 1].toString(true), isTemplate ? 10 : 828, { temporary: true });
|
|
231
|
+
title.fragment = undefined;
|
|
232
|
+
return title;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Get the module name and module function name
|
|
236
|
+
*
|
|
237
|
+
* 获取模块名和模块函数名
|
|
238
|
+
* @throws `Error` 仅用于模块
|
|
239
|
+
*/
|
|
240
|
+
getModule() {
|
|
241
|
+
LSP: { // eslint-disable-line no-unused-labels
|
|
242
|
+
/* istanbul ignore if */
|
|
243
|
+
if (this.type !== 'magic-word' || this.name !== 'invoke') {
|
|
244
|
+
throw new Error('TranscludeToken.getModule method is only for modules!');
|
|
245
|
+
}
|
|
246
|
+
return [
|
|
247
|
+
this.#getTitle().title,
|
|
248
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
249
|
+
this.childNodes[2]?.toString(true).trim(),
|
|
250
|
+
];
|
|
251
|
+
}
|
|
204
252
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
this.setAttribute('modifier', this.buildFromStr(this.modifier, constants_1.BuildMethod.String));
|
|
210
|
-
}
|
|
211
|
-
super.afterBuild();
|
|
212
|
-
if (this.isTemplate()) {
|
|
213
|
-
const isTemplate = this.type === 'template';
|
|
214
|
-
if (isTemplate) {
|
|
215
|
-
this.#title = this.#getTitle();
|
|
216
|
-
this.setAttribute('name', this.#title.title);
|
|
253
|
+
/** @private */
|
|
254
|
+
afterBuild() {
|
|
255
|
+
if (this.modifier.includes('\0')) {
|
|
256
|
+
this.setAttribute('modifier', this.buildFromStr(this.modifier, constants_1.BuildMethod.String));
|
|
217
257
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
* @implements
|
|
223
|
-
*/
|
|
224
|
-
const transcludeListener = (e, data) => {
|
|
225
|
-
const { prevTarget } = e, { oldKey, newKey } = data;
|
|
226
|
-
if (typeof oldKey === 'string') {
|
|
227
|
-
delete data.oldKey;
|
|
228
|
-
delete data.newKey;
|
|
229
|
-
}
|
|
230
|
-
if (prevTarget === this.firstChild && isTemplate) {
|
|
258
|
+
super.afterBuild();
|
|
259
|
+
if (this.isTemplate()) {
|
|
260
|
+
const isTemplate = this.type === 'template';
|
|
261
|
+
if (isTemplate) {
|
|
231
262
|
this.#title = this.#getTitle();
|
|
232
263
|
this.setAttribute('name', this.#title.title);
|
|
233
264
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
265
|
+
/* NOT FOR BROWSER */
|
|
266
|
+
/**
|
|
267
|
+
* 当事件bubble到`parameter`时,将`oldKey`和`newKey`保存进AstEventData。
|
|
268
|
+
* 当继续bubble到`template`时,处理并删除`oldKey`和`newKey`。
|
|
269
|
+
* @implements
|
|
270
|
+
*/
|
|
271
|
+
const transcludeListener = (e, data) => {
|
|
272
|
+
const { prevTarget } = e, { oldKey, newKey } = data;
|
|
273
|
+
if (typeof oldKey === 'string') {
|
|
274
|
+
delete data.oldKey;
|
|
275
|
+
delete data.newKey;
|
|
276
|
+
}
|
|
277
|
+
if (prevTarget === this.firstChild && isTemplate) {
|
|
278
|
+
this.#title = this.#getTitle();
|
|
279
|
+
this.setAttribute('name', this.#title.title);
|
|
280
|
+
}
|
|
281
|
+
else if (oldKey !== newKey && prevTarget instanceof parameter_1.ParameterToken) {
|
|
282
|
+
const oldArgs = this.getArgs(oldKey, false, false);
|
|
283
|
+
oldArgs.delete(prevTarget);
|
|
284
|
+
this.getArgs(newKey, false, false).add(prevTarget);
|
|
285
|
+
this.#keys.add(newKey);
|
|
286
|
+
if (oldArgs.size === 0) {
|
|
287
|
+
this.#keys.delete(oldKey);
|
|
288
|
+
}
|
|
241
289
|
}
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
this.addEventListener(['remove', 'insert', 'replace', 'text'], transcludeListener);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
/** @private */
|
|
248
|
-
toString(skip) {
|
|
249
|
-
return `{{${this.modifier}${this.type === 'magic-word'
|
|
250
|
-
? this.firstChild.toString(skip)
|
|
251
|
-
+ (this.length === 1 ? '' : this.#colon)
|
|
252
|
-
+ this.childNodes.slice(1).map(child => child.toString(skip)).join('|')
|
|
253
|
-
: super.toString(skip, '|')}}}`;
|
|
254
|
-
}
|
|
255
|
-
/** @private */
|
|
256
|
-
text() {
|
|
257
|
-
const { childNodes, length, firstChild, modifier, type, name } = this;
|
|
258
|
-
return type === 'magic-word' && name === 'vardefine'
|
|
259
|
-
? ''
|
|
260
|
-
: `{{${modifier}${type === 'magic-word'
|
|
261
|
-
? firstChild.text()
|
|
262
|
-
+ (length === 1 ? '' : this.#colon)
|
|
263
|
-
+ (0, string_1.text)(childNodes.slice(1), '|')
|
|
264
|
-
: super.text('|')}}}`;
|
|
265
|
-
}
|
|
266
|
-
/** @private */
|
|
267
|
-
getAttribute(key) {
|
|
268
|
-
switch (key) {
|
|
269
|
-
case 'padding':
|
|
270
|
-
return this.modifier.length + 2;
|
|
271
|
-
case 'title':
|
|
272
|
-
return this.#title;
|
|
273
|
-
case 'colon':
|
|
274
|
-
return this.#colon;
|
|
275
|
-
/* NOT FOR BROWSER */
|
|
276
|
-
case 'keys':
|
|
277
|
-
return this.#keys;
|
|
278
|
-
/* NOT FOR BROWSER END */
|
|
279
|
-
default:
|
|
280
|
-
return super.getAttribute(key);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
/** @private */
|
|
284
|
-
getGaps() {
|
|
285
|
-
return 1;
|
|
286
|
-
}
|
|
287
|
-
/** @private */
|
|
288
|
-
lint(start = this.getAbsoluteIndex(), re) {
|
|
289
|
-
const errors = super.lint(start, re);
|
|
290
|
-
if (!this.isTemplate()) {
|
|
291
|
-
return errors;
|
|
292
|
-
}
|
|
293
|
-
const { type, childNodes, length } = this, rect = new rect_1.BoundingRect(this, start), invoke = type === 'magic-word';
|
|
294
|
-
if (invoke && !this.#getTitle().valid) {
|
|
295
|
-
errors.push((0, lint_1.generateForChild)(childNodes[1], rect, 'invalid-invoke', 'illegal module name'));
|
|
296
|
-
}
|
|
297
|
-
else {
|
|
298
|
-
const child = childNodes[invoke ? 1 : 0], i = child.childNodes
|
|
299
|
-
.findIndex(c => c.type === 'text' && (0, string_1.decodeHtml)(c.data).includes('#')), textNode = child.childNodes[i];
|
|
300
|
-
if (textNode) {
|
|
301
|
-
const e = (0, lint_1.generateForChild)(child, rect, 'no-ignored', 'useless fragment');
|
|
302
|
-
e.fix = {
|
|
303
|
-
range: [
|
|
304
|
-
e.startIndex + child.getRelativeIndex(i) + textNode.data.indexOf('#'),
|
|
305
|
-
e.endIndex,
|
|
306
|
-
],
|
|
307
|
-
text: '',
|
|
308
|
-
desc: 'remove',
|
|
309
290
|
};
|
|
310
|
-
|
|
291
|
+
this.addEventListener(['remove', 'insert', 'replace', 'text'], transcludeListener);
|
|
311
292
|
}
|
|
312
293
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
return
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
294
|
+
/** @private */
|
|
295
|
+
toString(skip) {
|
|
296
|
+
return `{{${this.modifier}${this.type === 'magic-word'
|
|
297
|
+
? this.firstChild.toString(skip)
|
|
298
|
+
+ (this.length === 1 ? '' : this.#colon)
|
|
299
|
+
+ this.childNodes.slice(1).map(child => child.toString(skip)).join('|')
|
|
300
|
+
: super.toString(skip, '|')}}}`;
|
|
301
|
+
}
|
|
302
|
+
/** @private */
|
|
303
|
+
text() {
|
|
304
|
+
const { childNodes, length, firstChild, modifier, type, name } = this;
|
|
305
|
+
return type === 'magic-word' && name === 'vardefine'
|
|
306
|
+
? ''
|
|
307
|
+
: `{{${modifier}${type === 'magic-word'
|
|
308
|
+
? firstChild.text()
|
|
309
|
+
+ (length === 1 ? '' : this.#colon)
|
|
310
|
+
+ (0, string_1.text)(childNodes.slice(1), '|')
|
|
311
|
+
: super.text('|')}}}`;
|
|
312
|
+
}
|
|
313
|
+
/** @private */
|
|
314
|
+
getAttribute(key) {
|
|
315
|
+
switch (key) {
|
|
316
|
+
case 'padding':
|
|
317
|
+
return this.modifier.length + 2;
|
|
318
|
+
case 'title':
|
|
319
|
+
return this.#title;
|
|
320
|
+
case 'colon':
|
|
321
|
+
return this.#colon;
|
|
322
|
+
/* NOT FOR BROWSER */
|
|
323
|
+
case 'keys':
|
|
324
|
+
return this.#keys;
|
|
325
|
+
/* NOT FOR BROWSER END */
|
|
326
|
+
default:
|
|
327
|
+
return super.getAttribute(key);
|
|
326
328
|
}
|
|
327
329
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
* 处理匿名参数更改
|
|
332
|
-
* @param addedToken 新增的参数
|
|
333
|
-
*/
|
|
334
|
-
#handleAnonArgChange(addedToken) {
|
|
335
|
-
const args = this.getAnonArgs(), added = typeof addedToken !== 'number';
|
|
336
|
-
/* NOT FOR BROWSER */
|
|
337
|
-
const maxAnon = String(args.length + (added ? 0 : 1));
|
|
338
|
-
if (added) {
|
|
339
|
-
this.#keys.add(maxAnon);
|
|
330
|
+
/** @private */
|
|
331
|
+
getGaps() {
|
|
332
|
+
return 1;
|
|
340
333
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
334
|
+
/** @private */
|
|
335
|
+
lint(start = this.getAbsoluteIndex(), re) {
|
|
336
|
+
const errors = super.lint(start, re);
|
|
337
|
+
if (!this.isTemplate()) {
|
|
338
|
+
return errors;
|
|
339
|
+
}
|
|
340
|
+
const { type, childNodes, length } = this, rect = new rect_1.BoundingRect(this, start), invoke = type === 'magic-word';
|
|
341
|
+
if (invoke && !this.#getTitle().valid) {
|
|
342
|
+
errors.push((0, lint_1.generateForChild)(childNodes[1], rect, 'invalid-invoke', 'illegal module name'));
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
const child = childNodes[invoke ? 1 : 0], i = child.childNodes
|
|
346
|
+
.findIndex(c => c.type === 'text' && (0, string_1.decodeHtml)(c.data).includes('#')), textNode = child.childNodes[i];
|
|
347
|
+
if (textNode) {
|
|
348
|
+
const e = (0, lint_1.generateForChild)(child, rect, 'no-ignored', 'useless fragment');
|
|
349
|
+
e.fix = {
|
|
350
|
+
range: [
|
|
351
|
+
e.startIndex + child.getRelativeIndex(i) + textNode.data.indexOf('#'),
|
|
352
|
+
e.endIndex,
|
|
353
|
+
],
|
|
354
|
+
text: '',
|
|
355
|
+
desc: 'remove',
|
|
356
|
+
};
|
|
357
|
+
errors.push(e);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (invoke && length === 2) {
|
|
361
|
+
errors.push((0, lint_1.generateForSelf)(this, rect, 'invalid-invoke', 'missing module function'));
|
|
362
|
+
return errors;
|
|
363
|
+
}
|
|
364
|
+
const duplicatedArgs = this.getDuplicatedArgs()
|
|
365
|
+
.filter(([, parameter]) => !parameter[0].querySelector('ext'));
|
|
366
|
+
if (duplicatedArgs.length > 0) {
|
|
367
|
+
for (const [, args] of duplicatedArgs) {
|
|
368
|
+
errors.push(...args.map(arg => {
|
|
369
|
+
const e = (0, lint_1.generateForChild)(arg, rect, 'no-duplicate', 'duplicated parameter');
|
|
370
|
+
e.suggestions = [{ desc: 'remove', range: [e.startIndex - 1, e.endIndex], text: '' }];
|
|
371
|
+
return e;
|
|
372
|
+
}));
|
|
353
373
|
}
|
|
354
374
|
}
|
|
375
|
+
return errors;
|
|
355
376
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
insertAt(token, i = this.length) {
|
|
363
|
-
super.insertAt(token, i);
|
|
364
|
-
if (token.anon) {
|
|
365
|
-
this.#handleAnonArgChange(token);
|
|
366
|
-
}
|
|
367
|
-
else if (token.name) {
|
|
368
|
-
this.getArgs(token.name, false, false).add(token);
|
|
377
|
+
/**
|
|
378
|
+
* 处理匿名参数更改
|
|
379
|
+
* @param addedToken 新增的参数
|
|
380
|
+
*/
|
|
381
|
+
#handleAnonArgChange(addedToken) {
|
|
382
|
+
const args = this.getAnonArgs(), added = typeof addedToken !== 'number';
|
|
369
383
|
/* NOT FOR BROWSER */
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
/**
|
|
391
|
-
* Get parameters with the specified name
|
|
392
|
-
*
|
|
393
|
-
* 获取指定参数
|
|
394
|
-
* @param key parameter name / 参数名
|
|
395
|
-
* @param exact whether to match anonymosity / 是否匹配匿名性
|
|
396
|
-
* @param copy whether to return a copy / 是否返回一个备份
|
|
397
|
-
*/
|
|
398
|
-
getArgs(key, exact, copy = true) {
|
|
399
|
-
const keyStr = String(key)
|
|
400
|
-
.replace(/^[ \t\n\0\v]+|([^ \t\n\0\v])[ \t\n\0\v]+$/gu, '$1');
|
|
401
|
-
let args;
|
|
402
|
-
if (this.#args.has(keyStr)) {
|
|
403
|
-
args = this.#args.get(keyStr);
|
|
404
|
-
}
|
|
405
|
-
else {
|
|
406
|
-
args = new Set(this.getAllArgs().filter(({ name }) => keyStr === name));
|
|
407
|
-
this.#args.set(keyStr, args);
|
|
408
|
-
}
|
|
409
|
-
/* NOT FOR BROWSER */
|
|
410
|
-
if (exact && keyStr.trim() && !isNaN(keyStr)) {
|
|
411
|
-
args = new Set([...args].filter(({ anon }) => typeof key === 'number' === anon));
|
|
384
|
+
const maxAnon = String(args.length + (added ? 0 : 1));
|
|
385
|
+
if (added) {
|
|
386
|
+
this.#keys.add(maxAnon);
|
|
387
|
+
}
|
|
388
|
+
else if (!this.hasArg(maxAnon, true)) {
|
|
389
|
+
this.#keys.delete(maxAnon);
|
|
390
|
+
}
|
|
391
|
+
/* NOT FOR BROWSER END */
|
|
392
|
+
for (let i = added ? args.indexOf(addedToken) : addedToken - 1; i < args.length; i++) {
|
|
393
|
+
const token = args[i], { name } = token, newName = String(i + 1);
|
|
394
|
+
if (name !== newName || token === addedToken) {
|
|
395
|
+
token.setAttribute('name', newName);
|
|
396
|
+
this.getArgs(newName, false, false).add(token);
|
|
397
|
+
/* NOT FOR BROWSER */
|
|
398
|
+
if (name && token !== addedToken) {
|
|
399
|
+
this.getArgs(name, false, false).delete(token);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
412
403
|
}
|
|
413
|
-
|
|
414
|
-
|
|
404
|
+
/**
|
|
405
|
+
* @override
|
|
406
|
+
* @param token node to be inserted / 待插入的子节点
|
|
407
|
+
* @param i position to be inserted at / 插入位置
|
|
408
|
+
*/
|
|
409
|
+
insertAt(token, i = this.length) {
|
|
410
|
+
super.insertAt(token, i);
|
|
411
|
+
if (token.anon) {
|
|
412
|
+
this.#handleAnonArgChange(token);
|
|
413
|
+
}
|
|
414
|
+
else if (token.name) {
|
|
415
|
+
this.getArgs(token.name, false, false).add(token);
|
|
416
|
+
/* NOT FOR BROWSER */
|
|
417
|
+
this.#keys.add(token.name);
|
|
418
|
+
}
|
|
419
|
+
return token;
|
|
415
420
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
421
|
+
/**
|
|
422
|
+
* Get all parameters
|
|
423
|
+
*
|
|
424
|
+
* 获取所有参数
|
|
425
|
+
*/
|
|
426
|
+
getAllArgs() {
|
|
427
|
+
return this.childNodes.filter((0, debug_1.isToken)('parameter'));
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Get all anonymous parameters
|
|
431
|
+
*
|
|
432
|
+
* 获取所有匿名参数
|
|
433
|
+
*/
|
|
434
|
+
getAnonArgs() {
|
|
435
|
+
return this.getAllArgs().filter(({ anon }) => anon);
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Get parameters with the specified name
|
|
439
|
+
*
|
|
440
|
+
* 获取指定参数
|
|
441
|
+
* @param key parameter name / 参数名
|
|
442
|
+
* @param exact whether to match anonymosity / 是否匹配匿名性
|
|
443
|
+
* @param copy whether to return a copy / 是否返回一个备份
|
|
444
|
+
*/
|
|
445
|
+
getArgs(key, exact, copy = true) {
|
|
446
|
+
const keyStr = String(key)
|
|
447
|
+
.replace(/^[ \t\n\0\v]+|([^ \t\n\0\v])[ \t\n\0\v]+$/gu, '$1');
|
|
448
|
+
let args;
|
|
449
|
+
if (this.#args.has(keyStr)) {
|
|
450
|
+
args = this.#args.get(keyStr);
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
args = new Set(this.getAllArgs().filter(({ name }) => keyStr === name));
|
|
454
|
+
this.#args.set(keyStr, args);
|
|
455
|
+
}
|
|
456
|
+
/* NOT FOR BROWSER */
|
|
457
|
+
if (exact && keyStr.trim() && !isNaN(keyStr)) {
|
|
458
|
+
args = new Set([...args].filter(({ anon }) => typeof key === 'number' === anon));
|
|
459
|
+
}
|
|
460
|
+
else if (copy) {
|
|
461
|
+
args = new Set(args);
|
|
462
|
+
}
|
|
463
|
+
/* NOT FOR BROWSER END */
|
|
464
|
+
return args;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Get duplicated parameters
|
|
468
|
+
*
|
|
469
|
+
* 获取重名参数
|
|
470
|
+
* @throws `Error` 仅用于模板
|
|
471
|
+
*/
|
|
472
|
+
getDuplicatedArgs() {
|
|
473
|
+
if (this.isTemplate()) {
|
|
474
|
+
return [...this.#args].filter(([, { size }]) => size > 1).map(([key, args]) => [key, [...args]]);
|
|
475
|
+
}
|
|
476
|
+
throw new Error('TranscludeToken.getDuplicatedArgs method is only for template!');
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Get possible values of some magic words
|
|
480
|
+
*
|
|
481
|
+
* 对特定魔术字获取可能的取值
|
|
482
|
+
* @throws `Error` 不是可接受的魔术字
|
|
483
|
+
*/
|
|
484
|
+
getPossibleValues() {
|
|
485
|
+
const { type, name, childNodes } = this;
|
|
486
|
+
if (type === 'template') {
|
|
454
487
|
throw new Error('TranscludeToken.getPossibleValues method is only for specific magic words!');
|
|
455
|
-
}
|
|
456
|
-
const queue = childNodes.slice(start, start + 2).map(({ childNodes: [, value] }) => value);
|
|
457
|
-
for (let i = 0; i < queue.length;) {
|
|
458
|
-
const { length, 0: first } = queue[i].childNodes.filter(child => child.text().trim());
|
|
459
|
-
if (length === 0) {
|
|
460
|
-
queue.splice(i, 1);
|
|
461
488
|
}
|
|
462
|
-
|
|
463
|
-
|
|
489
|
+
let start;
|
|
490
|
+
switch (name) {
|
|
491
|
+
case 'if':
|
|
492
|
+
case 'ifexist':
|
|
493
|
+
case 'ifexpr':
|
|
494
|
+
case 'iferror':
|
|
495
|
+
start = 2;
|
|
496
|
+
break;
|
|
497
|
+
case 'ifeq':
|
|
498
|
+
start = 3;
|
|
499
|
+
break;
|
|
500
|
+
default:
|
|
501
|
+
throw new Error('TranscludeToken.getPossibleValues method is only for specific magic words!');
|
|
464
502
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
i
|
|
503
|
+
const queue = childNodes.slice(start, start + 2).map(({ childNodes: [, value] }) => value);
|
|
504
|
+
for (let i = 0; i < queue.length;) {
|
|
505
|
+
const { length, 0: first } = queue[i].childNodes.filter(child => child.text().trim());
|
|
506
|
+
if (length === 0) {
|
|
507
|
+
queue.splice(i, 1);
|
|
470
508
|
}
|
|
471
|
-
|
|
509
|
+
else if (length > 1 || first.type !== 'magic-word') {
|
|
472
510
|
i++;
|
|
473
511
|
}
|
|
512
|
+
else {
|
|
513
|
+
try {
|
|
514
|
+
const possibleValues = first.getPossibleValues();
|
|
515
|
+
queue.splice(i, 1, ...possibleValues);
|
|
516
|
+
i += possibleValues.length;
|
|
517
|
+
}
|
|
518
|
+
catch {
|
|
519
|
+
i++;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
474
522
|
}
|
|
523
|
+
return queue;
|
|
475
524
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
525
|
+
/** @private */
|
|
526
|
+
print() {
|
|
527
|
+
const { childNodes, length, firstChild, modifier, type } = this;
|
|
528
|
+
return `<span class="wpb-${type}">{{${(0, string_1.escape)(modifier)}${type === 'magic-word'
|
|
529
|
+
? firstChild.print() + (length === 1 ? '' : this.#colon) + (0, string_1.print)(childNodes.slice(1), { sep: '|' })
|
|
530
|
+
: (0, string_1.print)(childNodes, { sep: '|' })}}}</span>`;
|
|
531
|
+
}
|
|
532
|
+
/* NOT FOR BROWSER */
|
|
533
|
+
cloneNode() {
|
|
534
|
+
const [first, ...cloned] = this.cloneChildNodes(), config = this.getAttribute('config');
|
|
535
|
+
return debug_1.Shadow.run(() => {
|
|
536
|
+
// @ts-expect-error abstract class
|
|
537
|
+
const token = new TranscludeToken(this.type === 'template' ? 'T' : first.text() + (cloned.length === 0 ? '' : this.#colon), [], config);
|
|
538
|
+
if (this.#raw) {
|
|
539
|
+
token.setModifier(this.modifier);
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
token.setAttribute('modifier', this.modifier);
|
|
543
|
+
}
|
|
544
|
+
token.firstChild.safeReplaceWith(first);
|
|
545
|
+
if (token.length > 1) {
|
|
546
|
+
token.removeAt(1);
|
|
547
|
+
}
|
|
548
|
+
token.append(...cloned);
|
|
549
|
+
return token;
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Convert to substitution
|
|
554
|
+
*
|
|
555
|
+
* 替换引用
|
|
556
|
+
*/
|
|
557
|
+
subst() {
|
|
558
|
+
this.setModifier('subst:');
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Convert to safe substitution
|
|
562
|
+
*
|
|
563
|
+
* 安全的替换引用
|
|
564
|
+
*/
|
|
565
|
+
safesubst() {
|
|
566
|
+
this.setModifier('safesubst:');
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* @override
|
|
570
|
+
* @param i position of the child node / 移除位置
|
|
571
|
+
*/
|
|
572
|
+
removeAt(i) {
|
|
573
|
+
const token = super.removeAt(i);
|
|
574
|
+
if (token.anon) {
|
|
575
|
+
this.#handleAnonArgChange(Number(token.name));
|
|
493
576
|
}
|
|
494
577
|
else {
|
|
495
|
-
token.
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
578
|
+
const args = this.getArgs(token.name, false, false);
|
|
579
|
+
args.delete(token);
|
|
580
|
+
if (args.size === 0) {
|
|
581
|
+
this.#keys.delete(token.name);
|
|
582
|
+
}
|
|
500
583
|
}
|
|
501
|
-
token.append(...cloned);
|
|
502
|
-
token.afterBuild();
|
|
503
584
|
return token;
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
/**
|
|
507
|
-
* Convert to substitution
|
|
508
|
-
*
|
|
509
|
-
* 替换引用
|
|
510
|
-
*/
|
|
511
|
-
subst() {
|
|
512
|
-
this.setModifier('subst:');
|
|
513
|
-
}
|
|
514
|
-
/**
|
|
515
|
-
* Convert to safe substitution
|
|
516
|
-
*
|
|
517
|
-
* 安全的替换引用
|
|
518
|
-
*/
|
|
519
|
-
safesubst() {
|
|
520
|
-
this.setModifier('safesubst:');
|
|
521
|
-
}
|
|
522
|
-
/**
|
|
523
|
-
* @override
|
|
524
|
-
* @param i position of the child node / 移除位置
|
|
525
|
-
*/
|
|
526
|
-
removeAt(i) {
|
|
527
|
-
const token = super.removeAt(i);
|
|
528
|
-
if (token.anon) {
|
|
529
|
-
this.#handleAnonArgChange(Number(token.name));
|
|
530
|
-
}
|
|
531
|
-
else {
|
|
532
|
-
const args = this.getArgs(token.name, false, false);
|
|
533
|
-
args.delete(token);
|
|
534
|
-
if (args.size === 0) {
|
|
535
|
-
this.#keys.delete(token.name);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
return token;
|
|
539
|
-
}
|
|
540
|
-
/**
|
|
541
|
-
* Check if there is a parameter with the specified name
|
|
542
|
-
*
|
|
543
|
-
* 是否具有某参数
|
|
544
|
-
* @param key parameter name / 参数名
|
|
545
|
-
* @param exact whether to match anonymosity / 是否匹配匿名性
|
|
546
|
-
*/
|
|
547
|
-
hasArg(key, exact) {
|
|
548
|
-
return this.getArgs(key, exact, false).size > 0;
|
|
549
|
-
}
|
|
550
|
-
/**
|
|
551
|
-
* Get the effective parameter with the specified name
|
|
552
|
-
*
|
|
553
|
-
* 获取生效的指定参数
|
|
554
|
-
* @param key parameter name / 参数名
|
|
555
|
-
* @param exact whether to match anonymosity / 是否匹配匿名性
|
|
556
|
-
*/
|
|
557
|
-
getArg(key, exact) {
|
|
558
|
-
return [...this.getArgs(key, exact, false)].sort((a, b) => a.compareDocumentPosition(b)).at(-1);
|
|
559
|
-
}
|
|
560
|
-
/**
|
|
561
|
-
* Remove parameters with the specified name
|
|
562
|
-
*
|
|
563
|
-
* 移除指定参数
|
|
564
|
-
* @param key parameter name / 参数名
|
|
565
|
-
* @param exact whether to match anonymosity / 是否匹配匿名性
|
|
566
|
-
*/
|
|
567
|
-
removeArg(key, exact) {
|
|
568
|
-
debug_1.Shadow.run(() => {
|
|
569
|
-
for (const token of this.getArgs(key, exact, false)) {
|
|
570
|
-
this.removeChild(token);
|
|
571
|
-
}
|
|
572
|
-
});
|
|
573
|
-
}
|
|
574
|
-
/**
|
|
575
|
-
* Get all parameter names
|
|
576
|
-
*
|
|
577
|
-
* 获取所有参数名
|
|
578
|
-
*/
|
|
579
|
-
getKeys() {
|
|
580
|
-
const args = this.getAllArgs();
|
|
581
|
-
if (this.#keys.size === 0 && args.length > 0) {
|
|
582
|
-
for (const { name } of args) {
|
|
583
|
-
this.#keys.add(name);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
return [...this.#keys];
|
|
587
|
-
}
|
|
588
|
-
/**
|
|
589
|
-
* Get parameter values
|
|
590
|
-
*
|
|
591
|
-
* 获取参数值
|
|
592
|
-
* @param key parameter name / 参数名
|
|
593
|
-
*/
|
|
594
|
-
getValues(key) {
|
|
595
|
-
return [...this.getArgs(key, false, false)].map(token => token.getValue());
|
|
596
|
-
}
|
|
597
|
-
getValue(key) {
|
|
598
|
-
return key === undefined
|
|
599
|
-
? Object.fromEntries(this.getKeys().map(k => [k, this.getValue(k)]))
|
|
600
|
-
: this.getArg(key)?.getValue();
|
|
601
|
-
}
|
|
602
|
-
/**
|
|
603
|
-
* Insert an anonymous parameter
|
|
604
|
-
*
|
|
605
|
-
* 插入匿名参数
|
|
606
|
-
* @param val parameter value / 参数值
|
|
607
|
-
*/
|
|
608
|
-
newAnonArg(val) {
|
|
609
|
-
require('../addon/transclude');
|
|
610
|
-
return this.newAnonArg(val);
|
|
611
|
-
}
|
|
612
|
-
/**
|
|
613
|
-
* Set the parameter value
|
|
614
|
-
*
|
|
615
|
-
* 设置参数值
|
|
616
|
-
* @param key parameter name / 参数名
|
|
617
|
-
* @param value parameter value / 参数值
|
|
618
|
-
* @throws `Error` 仅用于模板
|
|
619
|
-
*/
|
|
620
|
-
setValue(key, value) {
|
|
621
|
-
require('../addon/transclude');
|
|
622
|
-
this.setValue(key, value);
|
|
623
|
-
}
|
|
624
|
-
/**
|
|
625
|
-
* Convert all anonymous parameters to named ones
|
|
626
|
-
*
|
|
627
|
-
* 将匿名参数改写为命名参数
|
|
628
|
-
* @throws `Error` 仅用于模板
|
|
629
|
-
*/
|
|
630
|
-
anonToNamed() {
|
|
631
|
-
if (!this.isTemplate()) {
|
|
632
|
-
throw new Error('TranscludeToken.anonToNamed method is only for template!');
|
|
633
|
-
}
|
|
634
|
-
for (const token of this.getAnonArgs()) {
|
|
635
|
-
token.firstChild.replaceChildren(token.name);
|
|
636
585
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
586
|
+
/**
|
|
587
|
+
* Check if there is a parameter with the specified name
|
|
588
|
+
*
|
|
589
|
+
* 是否具有某参数
|
|
590
|
+
* @param key parameter name / 参数名
|
|
591
|
+
* @param exact whether to match anonymosity / 是否匹配匿名性
|
|
592
|
+
*/
|
|
593
|
+
hasArg(key, exact) {
|
|
594
|
+
return this.getArgs(key, exact, false).size > 0;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Get the effective parameter with the specified name
|
|
598
|
+
*
|
|
599
|
+
* 获取生效的指定参数
|
|
600
|
+
* @param key parameter name / 参数名
|
|
601
|
+
* @param exact whether to match anonymosity / 是否匹配匿名性
|
|
602
|
+
*/
|
|
603
|
+
getArg(key, exact) {
|
|
604
|
+
return [...this.getArgs(key, exact, false)].sort((a, b) => a.compareDocumentPosition(b)).at(-1);
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Remove parameters with the specified name
|
|
608
|
+
*
|
|
609
|
+
* 移除指定参数
|
|
610
|
+
* @param key parameter name / 参数名
|
|
611
|
+
* @param exact whether to match anonymosity / 是否匹配匿名性
|
|
612
|
+
*/
|
|
613
|
+
removeArg(key, exact) {
|
|
614
|
+
debug_1.Shadow.run(() => {
|
|
615
|
+
for (const token of this.getArgs(key, exact, false)) {
|
|
616
|
+
this.removeChild(token);
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Get all parameter names
|
|
622
|
+
*
|
|
623
|
+
* 获取所有参数名
|
|
624
|
+
*/
|
|
625
|
+
getKeys() {
|
|
626
|
+
const args = this.getAllArgs();
|
|
627
|
+
if (this.#keys.size === 0 && args.length > 0) {
|
|
628
|
+
for (const { name } of args) {
|
|
629
|
+
this.#keys.add(name);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return [...this.#keys];
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Get parameter values
|
|
636
|
+
*
|
|
637
|
+
* 获取参数值
|
|
638
|
+
* @param key parameter name / 参数名
|
|
639
|
+
*/
|
|
640
|
+
getValues(key) {
|
|
641
|
+
return [...this.getArgs(key, false, false)].map(token => token.getValue());
|
|
642
|
+
}
|
|
643
|
+
getValue(key) {
|
|
644
|
+
return key === undefined
|
|
645
|
+
? Object.fromEntries(this.getKeys().map(k => [k, this.getValue(k)]))
|
|
646
|
+
: this.getArg(key)?.getValue();
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Insert an anonymous parameter
|
|
650
|
+
*
|
|
651
|
+
* 插入匿名参数
|
|
652
|
+
* @param val parameter value / 参数值
|
|
653
|
+
*/
|
|
654
|
+
newAnonArg(val) {
|
|
655
|
+
require('../addon/transclude');
|
|
656
|
+
return this.newAnonArg(val);
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Set the parameter value
|
|
660
|
+
*
|
|
661
|
+
* 设置参数值
|
|
662
|
+
* @param key parameter name / 参数名
|
|
663
|
+
* @param value parameter value / 参数值
|
|
664
|
+
* @throws `Error` 仅用于模板
|
|
665
|
+
*/
|
|
666
|
+
setValue(key, value) {
|
|
667
|
+
require('../addon/transclude');
|
|
668
|
+
this.setValue(key, value);
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Convert all anonymous parameters to named ones
|
|
672
|
+
*
|
|
673
|
+
* 将匿名参数改写为命名参数
|
|
674
|
+
* @throws `Error` 仅用于模板
|
|
675
|
+
*/
|
|
676
|
+
anonToNamed() {
|
|
677
|
+
if (!this.isTemplate()) {
|
|
678
|
+
throw new Error('TranscludeToken.anonToNamed method is only for template!');
|
|
679
|
+
}
|
|
680
|
+
for (const token of this.getAnonArgs()) {
|
|
681
|
+
token.firstChild.replaceChildren(token.name);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Replace the template name
|
|
686
|
+
*
|
|
687
|
+
* 替换模板名
|
|
688
|
+
* @param title template name / 模板名
|
|
689
|
+
* @throws `Error` 仅用于模板
|
|
690
|
+
*/
|
|
691
|
+
replaceTemplate(title) {
|
|
692
|
+
require('../addon/transclude');
|
|
693
|
+
this.replaceTemplate(title);
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Replace the module name
|
|
697
|
+
*
|
|
698
|
+
* 替换模块名
|
|
699
|
+
* @param title module name / 模块名
|
|
700
|
+
* @throws `Error` 仅用于模块
|
|
701
|
+
*/
|
|
702
|
+
replaceModule(title) {
|
|
703
|
+
require('../addon/transclude');
|
|
704
|
+
this.replaceModule(title);
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Replace the module function
|
|
708
|
+
*
|
|
709
|
+
* 替换模块函数
|
|
710
|
+
* @param func module function name / 模块函数名
|
|
711
|
+
* @throws `Error` 仅用于模块
|
|
712
|
+
* @throws `Error` 尚未指定模块名称
|
|
713
|
+
*/
|
|
714
|
+
replaceFunction(func) {
|
|
715
|
+
require('../addon/transclude');
|
|
716
|
+
this.replaceFunction(func);
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Count duplicated parameters
|
|
720
|
+
*
|
|
721
|
+
* 重复参数计数
|
|
722
|
+
* @throws `Error` 仅用于模板
|
|
723
|
+
*/
|
|
724
|
+
hasDuplicatedArgs() {
|
|
725
|
+
if (this.isTemplate()) {
|
|
726
|
+
return this.getAllArgs().length - this.getKeys().length;
|
|
727
|
+
}
|
|
728
|
+
throw new Error('TranscludeToken.hasDuplicatedArgs method is only for template!');
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Fix duplicated parameters
|
|
732
|
+
* @description
|
|
733
|
+
* - Only empty parameters and identical parameters are removed with `aggressive = false`.
|
|
734
|
+
* Anonymous parameters have a higher precedence, otherwise all anonymous parameters are converted to named ones.
|
|
735
|
+
* - Additionally, consecutive numbered parameters are treated with `aggressive = true`.
|
|
736
|
+
*
|
|
737
|
+
* 修复重名参数
|
|
738
|
+
* @description
|
|
739
|
+
* - `aggressive = false`时只移除空参数和全同参数,优先保留匿名参数,否则将所有匿名参数更改为命名。
|
|
740
|
+
* - `aggressive = true`时还会尝试处理连续的以数字编号的参数。
|
|
741
|
+
* @param aggressive whether to use a more risky approach / 是否使用有更大风险的修复手段
|
|
742
|
+
*/
|
|
743
|
+
fixDuplication(aggressive) {
|
|
744
|
+
require('../addon/transclude');
|
|
745
|
+
return this.fixDuplication(aggressive);
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Escape tables inside the template
|
|
749
|
+
*
|
|
750
|
+
* 转义模板内的表格
|
|
751
|
+
* @throws `Error` 转义失败
|
|
752
|
+
*/
|
|
753
|
+
escapeTables() {
|
|
754
|
+
require('../addon/transclude');
|
|
755
|
+
return this.escapeTables();
|
|
756
|
+
}
|
|
757
|
+
/** @private */
|
|
758
|
+
toHtmlInternal(opt) {
|
|
759
|
+
const { type, name } = this;
|
|
760
|
+
if (type === 'template' && !name.startsWith('Special:')) {
|
|
761
|
+
if (this.normalizeTitle(name, 0, { halfParsed: true, temporary: true }).valid) {
|
|
762
|
+
const title = name.replaceAll('_', ' ');
|
|
763
|
+
return `<a href="${this.#title.getUrl()}?action=edit&redlink=1" class="new" title="${title} (page does not exist)">${title}</a>`;
|
|
764
|
+
}
|
|
765
|
+
const str = this.toString(true);
|
|
766
|
+
return opt?.nowrap ? str.replaceAll('\n', ' ') : str;
|
|
767
|
+
}
|
|
768
|
+
return basicMagicWords.has(name) ? basicMagicWords.get(name) : '';
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
return TranscludeToken = _classThis;
|
|
772
|
+
})();
|
|
725
773
|
exports.TranscludeToken = TranscludeToken;
|
|
726
774
|
constants_1.classes['TranscludeToken'] = __filename;
|