wikiparser-node 1.30.0 → 1.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/bundle/bundle-es8.min.js +28 -29
- package/bundle/bundle-lsp.min.js +34 -34
- package/bundle/bundle.min.js +23 -23
- package/dist/addon/attribute.js +166 -0
- package/dist/addon/link.js +92 -0
- package/dist/addon/table.js +12 -3
- package/dist/addon/token.js +5 -316
- package/dist/addon/transclude.js +11 -8
- package/dist/base.d.mts +13 -4
- package/dist/base.d.ts +13 -4
- package/dist/base.js +2 -0
- package/dist/base.mjs +2 -0
- package/dist/bin/config.js +1 -1
- package/dist/index.d.ts +34 -5
- package/dist/index.js +100 -88
- package/dist/internal.d.ts +6 -1
- package/dist/lib/document.d.ts +9 -7
- package/dist/lib/document.js +91 -66
- package/dist/lib/element.d.ts +8 -8
- package/dist/lib/element.js +52 -50
- package/dist/lib/lintConfig.js +6 -2
- package/dist/lib/lsp.js +120 -56
- package/dist/lib/node.js +21 -15
- package/dist/lib/redirectMap.js +1 -1
- package/dist/lib/text.js +17 -14
- package/dist/lib/title.d.ts +11 -0
- package/dist/lib/title.js +37 -13
- package/dist/mixin/elementLike.js +2 -3
- package/dist/mixin/syntax.js +13 -7
- package/dist/parser/commentAndExt.js +4 -4
- package/dist/parser/hrAndDoubleUnderscore.js +11 -9
- package/dist/parser/selector.js +16 -41
- package/dist/render/expand.js +216 -0
- package/dist/render/extension.js +141 -0
- package/dist/render/html.js +91 -0
- package/dist/render/magicWords.js +768 -0
- package/dist/render/syntaxhighlight.js +415 -0
- package/dist/src/arg.js +8 -1
- package/dist/src/atom.js +2 -0
- package/dist/src/attribute.d.ts +5 -0
- package/dist/src/attribute.js +59 -104
- package/dist/src/attributes.d.ts +5 -0
- package/dist/src/attributes.js +17 -51
- package/dist/src/converter.js +4 -2
- package/dist/src/converterFlags.js +8 -6
- package/dist/src/converterRule.js +19 -15
- package/dist/src/extLink.js +2 -2
- package/dist/src/heading.d.ts +1 -1
- package/dist/src/heading.js +5 -3
- package/dist/src/imageParameter.d.ts +0 -1
- package/dist/src/imageParameter.js +34 -24
- package/dist/src/index.d.ts +5 -5
- package/dist/src/index.js +37 -27
- package/dist/src/link/base.js +26 -36
- package/dist/src/link/category.d.ts +8 -1
- package/dist/src/link/category.js +104 -38
- package/dist/src/link/file.js +21 -8
- package/dist/src/link/galleryImage.js +2 -3
- package/dist/src/link/index.d.ts +1 -1
- package/dist/src/link/index.js +12 -33
- package/dist/src/multiLine/index.js +1 -1
- package/dist/src/nowiki/comment.js +1 -1
- package/dist/src/nowiki/doubleUnderscore.js +4 -2
- package/dist/src/nowiki/index.js +34 -31
- package/dist/src/nowiki/list.js +3 -1
- package/dist/src/nowiki/listBase.d.ts +2 -1
- package/dist/src/nowiki/listBase.js +22 -8
- package/dist/src/nowiki/quote.d.ts +1 -1
- package/dist/src/nowiki/quote.js +3 -3
- package/dist/src/onlyinclude.js +1 -1
- package/dist/src/paramLine.js +2 -0
- package/dist/src/parameter.js +1 -1
- package/dist/src/redirect.js +1 -1
- package/dist/src/table/base.js +5 -4
- package/dist/src/table/index.d.ts +1 -6
- package/dist/src/table/index.js +7 -16
- package/dist/src/table/td.js +12 -9
- package/dist/src/tag/html.js +2 -3
- package/dist/src/tag/index.js +8 -6
- package/dist/src/tagPair/ext.js +18 -41
- package/dist/src/tagPair/index.js +6 -4
- package/dist/src/tagPair/translate.d.ts +1 -0
- package/dist/src/tagPair/translate.js +10 -3
- package/dist/src/transclude.js +18 -21
- package/dist/util/constants.js +9 -3
- package/dist/util/debug.js +72 -5
- package/dist/util/diff.js +17 -15
- package/dist/util/html.js +14 -5
- package/dist/util/selector.js +37 -0
- package/dist/util/sharable.d.mts +4 -1
- package/dist/util/sharable.js +7 -7
- package/dist/util/sharable.mjs +7 -7
- package/dist/util/string.js +51 -20
- package/extensions/dist/base.js +2 -2
- package/extensions/editor.css +1 -1
- package/i18n/en.json +1 -0
- package/i18n/zh-hans.json +30 -29
- package/i18n/zh-hant.json +31 -30
- package/package.json +23 -16
- package/dist/addon/magicWords.js +0 -132
|
@@ -5,7 +5,6 @@ const common_1 = require("@bhsd/common");
|
|
|
5
5
|
const string_1 = require("../util/string");
|
|
6
6
|
const onlyinclude_1 = require("../src/onlyinclude");
|
|
7
7
|
const noinclude_1 = require("../src/nowiki/noinclude");
|
|
8
|
-
const translate_1 = require("../src/tagPair/translate");
|
|
9
8
|
const include_1 = require("../src/tagPair/include");
|
|
10
9
|
const ext_1 = require("../src/tagPair/ext");
|
|
11
10
|
const comment_1 = require("../src/nowiki/comment");
|
|
@@ -49,7 +48,7 @@ const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
|
|
|
49
48
|
str += `\0${accum.length - 1}n\x7F`;
|
|
50
49
|
};
|
|
51
50
|
while (i !== -1 && j !== -1) {
|
|
52
|
-
const token = `\0${accum.length}
|
|
51
|
+
const token = `\0${accum.length}g\x7F`;
|
|
53
52
|
new onlyinclude_1.OnlyincludeToken(wikitext.slice(i + length, j), config, accum);
|
|
54
53
|
if (i > 0) {
|
|
55
54
|
noinclude(wikitext.slice(0, i));
|
|
@@ -67,16 +66,17 @@ const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
|
|
|
67
66
|
const { ext } = config;
|
|
68
67
|
let newExt = ext, newConfig = config;
|
|
69
68
|
if (ext.includes('translate')) {
|
|
69
|
+
const { TranslateToken } = require('../src/tagPair/translate');
|
|
70
|
+
const stack = [];
|
|
70
71
|
newExt = ext.filter(e => e !== 'translate' && e !== 'tvar');
|
|
71
72
|
newConfig = { ...config, ext: newExt };
|
|
72
|
-
const stack = [];
|
|
73
73
|
wikitext = wikitext.replace(/<nowiki>[\s\S]*?<\/nowiki>/giu, m => {
|
|
74
74
|
stack.push(m);
|
|
75
75
|
return `\0${stack.length - 1}\x7F`;
|
|
76
76
|
}).replace(/<translate( nowrap)?>([\s\S]*?)<\/translate>/gu, (_, p1, p2) => {
|
|
77
77
|
const l = accum.length;
|
|
78
78
|
// @ts-expect-error abstract class
|
|
79
|
-
new
|
|
79
|
+
new TranslateToken(p1, p2 && (0, string_1.restore)(p2, stack), newConfig, accum);
|
|
80
80
|
return `\0${l}g\x7F`;
|
|
81
81
|
});
|
|
82
82
|
wikitext = (0, string_1.restore)(wikitext, stack);
|
|
@@ -18,20 +18,22 @@ const parseHrAndDoubleUnderscore = ({ firstChild: { data }, type, name }, config
|
|
|
18
18
|
const { doubleUnderscore: [insensitive, sensitive, aliases] } = config, all = [...insensitive, ...sensitive];
|
|
19
19
|
config.insensitiveDoubleUnderscore ??= new Set(insensitive.filter(cm_util_1.isUnderscore));
|
|
20
20
|
config.sensitiveDoubleUnderscore ??= new Set(sensitive.filter(cm_util_1.isUnderscore));
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
22
|
+
/^((?:\0\d+[cno]\x7F)*)(-{4,})|__(toc|notoc)__|_{2}(目次)_{2}/gimu;
|
|
23
|
+
config.regexHrAndDoubleUnderscore ??= new RegExp(String.raw `^((?:\0\d+[cno]\x7F)*)(-{4,})|__(${all.filter(cm_util_1.isUnderscore).join('|')})__|_{2}(${all.filter(s => !(0, cm_util_1.isUnderscore)(s)).map(s => s.slice(2, -2)).join('|')})_{2}`, 'gimu');
|
|
23
24
|
if (type !== 'root' && (type !== 'ext-inner' || name !== 'poem')) {
|
|
24
25
|
data = `\0${data}`;
|
|
25
26
|
}
|
|
26
|
-
data = data.replace(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
data = data.replace(config.regexHrAndDoubleUnderscore, (m, p1, p2, p3, p4) => {
|
|
28
|
+
if (p2) {
|
|
29
|
+
// @ts-expect-error abstract class
|
|
30
|
+
new hr_1.HrToken(p2, config, accum);
|
|
31
|
+
return `${p1}\0${accum.length - 1}r\x7F`;
|
|
32
|
+
}
|
|
33
|
+
const key = p3 ?? p4, caseSensitive = config.sensitiveDoubleUnderscore.has(key), lc = key.toLowerCase(), caseInsensitive = config.insensitiveDoubleUnderscore.has(lc);
|
|
32
34
|
if (caseSensitive || caseInsensitive) {
|
|
33
35
|
// @ts-expect-error abstract class
|
|
34
|
-
new doubleUnderscore_1.DoubleUnderscoreToken(key, caseSensitive, Boolean(
|
|
36
|
+
new doubleUnderscore_1.DoubleUnderscoreToken(key, caseSensitive, Boolean(p4), config, accum);
|
|
35
37
|
return `\0${accum.length - 1}${caseInsensitive && (aliases?.[lc] ?? /* istanbul ignore next */ lc) === 'toc' ? 'u' : 'n'}\x7F`;
|
|
36
38
|
}
|
|
37
39
|
return m;
|
package/dist/parser/selector.js
CHANGED
|
@@ -1,24 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
/* NOT FOR BROWSER */
|
|
3
|
+
exports.checkToken = void 0;
|
|
5
4
|
const constants_1 = require("../util/constants");
|
|
5
|
+
const selector_1 = require("../util/selector");
|
|
6
6
|
const ranges_1 = require("../lib/ranges");
|
|
7
7
|
const title_1 = require("../lib/title");
|
|
8
8
|
const attributes_1 = require("../lib/attributes");
|
|
9
|
-
/* NOT FOR BROWSER END */
|
|
10
|
-
/**
|
|
11
|
-
* type和name选择器
|
|
12
|
-
* @param selector
|
|
13
|
-
*/
|
|
14
|
-
const basic = (selector) => {
|
|
15
|
-
if (selector.includes('#')) {
|
|
16
|
-
const i = selector.indexOf('#'), targetType = selector.slice(0, i), targetName = selector.slice(i + 1);
|
|
17
|
-
return (type, name) => (i === 0 || type === targetType) && name === targetName;
|
|
18
|
-
}
|
|
19
|
-
return selector ? (type) => type === selector : () => true;
|
|
20
|
-
};
|
|
21
|
-
/* NOT FOR BROWSER */
|
|
22
9
|
const simplePseudos = new Set([
|
|
23
10
|
'root',
|
|
24
11
|
'first-child',
|
|
@@ -172,7 +159,7 @@ const matches = (token, step, scope, has) => {
|
|
|
172
159
|
}
|
|
173
160
|
return token === scope;
|
|
174
161
|
default:
|
|
175
|
-
return basic(selector)(type, name);
|
|
162
|
+
return (0, selector_1.basic)(selector)(type, name);
|
|
176
163
|
}
|
|
177
164
|
}
|
|
178
165
|
else if (selector.length === 4) { // 情形2:属性选择器
|
|
@@ -212,9 +199,9 @@ const matches = (token, step, scope, has) => {
|
|
|
212
199
|
const [s, pseudo] = selector; // 情形3:复杂伪选择器
|
|
213
200
|
switch (pseudo) {
|
|
214
201
|
case 'is':
|
|
215
|
-
return (0,
|
|
202
|
+
return (0, selector_1.getCondition)(s, scope)(token);
|
|
216
203
|
case 'not':
|
|
217
|
-
return !(0,
|
|
204
|
+
return !(0, selector_1.getCondition)(s, scope)(token);
|
|
218
205
|
case 'nth-child':
|
|
219
206
|
return nth(s, attributes.index);
|
|
220
207
|
case 'nth-of-type':
|
|
@@ -230,7 +217,7 @@ const matches = (token, step, scope, has) => {
|
|
|
230
217
|
if (has) {
|
|
231
218
|
throw new SyntaxError('The :has() pseudo-selector cannot be nested.');
|
|
232
219
|
}
|
|
233
|
-
const condition = (0,
|
|
220
|
+
const condition = (0, selector_1.getCondition)(s, scope, token), childOrSibling = attributes.siblings && /(?:^|,)\s*[+~]/u.test(s)
|
|
234
221
|
? [...token.childNodes, ...attributes.siblings.slice(attributes.siblings.indexOf(token))]
|
|
235
222
|
: token.childNodes;
|
|
236
223
|
/**
|
|
@@ -308,8 +295,10 @@ const matchesArray = (token, copy, scope, has) => {
|
|
|
308
295
|
* @param selector
|
|
309
296
|
*/
|
|
310
297
|
const desanitize = (selector) => {
|
|
311
|
-
|
|
312
|
-
|
|
298
|
+
if (selector.includes('&') && selector.includes(';')) {
|
|
299
|
+
for (const [c, entity] of specialChars) {
|
|
300
|
+
selector = selector.replaceAll(entity, c);
|
|
301
|
+
}
|
|
313
302
|
}
|
|
314
303
|
return selector.trim();
|
|
315
304
|
};
|
|
@@ -326,8 +315,10 @@ const deQuote = (val) => /^(["']).*\1$/u.test(val) ? val.slice(1, -1) : val.trim
|
|
|
326
315
|
*/
|
|
327
316
|
const checkToken = (selector, scope, has) => (token) => {
|
|
328
317
|
let sanitized = selector.trim();
|
|
329
|
-
|
|
330
|
-
|
|
318
|
+
if (sanitized.includes('\\')) {
|
|
319
|
+
for (const [c, entity] of specialChars) {
|
|
320
|
+
sanitized = sanitized.replaceAll(`\\${c}`, entity);
|
|
321
|
+
}
|
|
331
322
|
}
|
|
332
323
|
const stack = [[[]]];
|
|
333
324
|
let regex = regularRegex, mt = regex.exec(sanitized), [condition] = stack, [step] = condition;
|
|
@@ -445,21 +436,5 @@ const checkToken = (selector, scope, has) => (token) => {
|
|
|
445
436
|
/* istanbul ignore next */
|
|
446
437
|
throw new SyntaxError(`Unclosed '${regex === attributeRegex ? '[' : '('}' in the selector!\n${desanitize(sanitized)}`);
|
|
447
438
|
};
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
* 将选择器转化为类型谓词
|
|
451
|
-
* @param selector 选择器
|
|
452
|
-
* @param scope 作用对象
|
|
453
|
-
* @param has `:has()`伪选择器
|
|
454
|
-
*/
|
|
455
|
-
const getCondition = (selector, scope, has) => {
|
|
456
|
-
/* NOT FOR BROWSER */
|
|
457
|
-
if (/[^a-z\-,#\s]|(?<![\s,])\s+(?![\s,])/u.test(selector.trim())) {
|
|
458
|
-
return checkToken(selector, scope, has);
|
|
459
|
-
}
|
|
460
|
-
/* NOT FOR BROWSER END */
|
|
461
|
-
const parts = selector.split(',').map(str => basic(str.trim()));
|
|
462
|
-
return (({ type, name }) => parts.some(condition => condition(type, name)));
|
|
463
|
-
};
|
|
464
|
-
exports.getCondition = getCondition;
|
|
465
|
-
constants_1.parsers['parseSelector'] = __filename;
|
|
439
|
+
exports.checkToken = checkToken;
|
|
440
|
+
constants_1.parsers['checkToken'] = __filename;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.expandToken = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const constants_1 = require("../util/constants");
|
|
10
|
+
const debug_1 = require("../util/debug");
|
|
11
|
+
const string_1 = require("../util/string");
|
|
12
|
+
const redirect_1 = require("../parser/redirect");
|
|
13
|
+
const magicWords_1 = require("./magicWords");
|
|
14
|
+
const index_1 = __importDefault(require("../index"));
|
|
15
|
+
const index_2 = require("../src/index");
|
|
16
|
+
const solvedMagicWords = new Set([
|
|
17
|
+
'if',
|
|
18
|
+
'ifeq',
|
|
19
|
+
'ifexist',
|
|
20
|
+
'iferror',
|
|
21
|
+
'switch',
|
|
22
|
+
]);
|
|
23
|
+
/**
|
|
24
|
+
* 隐式换行
|
|
25
|
+
* @param str 字符串
|
|
26
|
+
* @param prev 前一个字符
|
|
27
|
+
*/
|
|
28
|
+
const implicitNewLine = (str, prev) => prev + (prev !== '\n' && /^(?:\{\||[:;#*])/u.test(str) ? `\n${str}` : str);
|
|
29
|
+
/**
|
|
30
|
+
* 加载模板
|
|
31
|
+
* @param title 模板名
|
|
32
|
+
* @param config
|
|
33
|
+
*/
|
|
34
|
+
const loadTemplate = (title, config) => {
|
|
35
|
+
if (index_1.default.templates.has(title)) {
|
|
36
|
+
return title;
|
|
37
|
+
}
|
|
38
|
+
else if (index_1.default.templateDir === undefined) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
else if (!path_1.default.isAbsolute(index_1.default.templateDir)) {
|
|
42
|
+
index_1.default.templateDir = path_1.default.join(__dirname, '..', '..', index_1.default.templateDir);
|
|
43
|
+
}
|
|
44
|
+
const file = fs_1.default.readdirSync(index_1.default.templateDir, { withFileTypes: true, recursive: true })
|
|
45
|
+
.filter(dirent => dirent.isFile())
|
|
46
|
+
.find(({ name, parentPath }) => {
|
|
47
|
+
const t = path_1.default.relative(index_1.default.templateDir, path_1.default.join(parentPath, name.replace(/\.(?:wiki|txt)$/iu, ''))).replaceAll('꞉', ':');
|
|
48
|
+
try {
|
|
49
|
+
return decodeURIComponent(t) === title;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return t === title;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
if (!file) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const content = (0, string_1.tidy)(fs_1.default.readFileSync(path_1.default.join(file.parentPath, file.name), 'utf8')), accum = [], parsed = debug_1.Shadow.run(() => (0, redirect_1.parseRedirect)(content, config, accum));
|
|
59
|
+
if (parsed) {
|
|
60
|
+
return loadTemplate(accum[0].lastChild.getTitle().title, config);
|
|
61
|
+
}
|
|
62
|
+
index_1.default.templates.set(title, content);
|
|
63
|
+
return title;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* 清理已展开的节点
|
|
67
|
+
* @param accum
|
|
68
|
+
* @param tokens 要清理的节点
|
|
69
|
+
*/
|
|
70
|
+
const clean = (accum, tokens) => {
|
|
71
|
+
for (const t of Array.isArray(tokens) ? tokens.map(({ lastChild }) => lastChild) : [tokens]) {
|
|
72
|
+
// @ts-expect-error sparse array
|
|
73
|
+
accum[accum.indexOf(t)] = undefined;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* 展开模板
|
|
78
|
+
* @param wikitext
|
|
79
|
+
* @param page 页面名称
|
|
80
|
+
* @param callPage 调用页面名称
|
|
81
|
+
* @param config
|
|
82
|
+
* @param include
|
|
83
|
+
* @param context 模板调用环境
|
|
84
|
+
* @param now 当前时间
|
|
85
|
+
* @param accum
|
|
86
|
+
* @param stack 模板调用栈
|
|
87
|
+
*/
|
|
88
|
+
const expand = (wikitext, page, callPage, config, include, context, now = index_1.default.now, accum = [], stack = []) => {
|
|
89
|
+
const n = accum.length, token = new index_2.Token(wikitext, { ...config, inExt: true }, accum);
|
|
90
|
+
token.type = 'root';
|
|
91
|
+
token.pageName = page;
|
|
92
|
+
token.parseOnce(0, include);
|
|
93
|
+
if (context !== false) {
|
|
94
|
+
for (const plain of [...accum.slice(n), token]) {
|
|
95
|
+
if (plain.length !== 1 || plain.firstChild.type !== 'text') {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const { data } = plain.firstChild;
|
|
99
|
+
if (!/\0\d+g\x7F/u.test(data)) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const expanded = data.replace(/\0(\d+)g\x7F/gu, (_, i) => {
|
|
103
|
+
const target = accum[i];
|
|
104
|
+
if (target.type === 'onlyinclude') {
|
|
105
|
+
clean(accum, target);
|
|
106
|
+
return target.firstChild.toString();
|
|
107
|
+
}
|
|
108
|
+
const { lastChild } = target;
|
|
109
|
+
clean(accum, lastChild);
|
|
110
|
+
return lastChild.firstChild.toString().replace(/\0(\d+)c\x7F[\n ]|\0(\d+)n\x7F|^\n|\n$/gu, (m, p1, p2) => {
|
|
111
|
+
if (p1 !== undefined) {
|
|
112
|
+
const { innerText } = accum[p1];
|
|
113
|
+
return /^T:[^_/\n<>~]+$/u.test(innerText) ? '' : m;
|
|
114
|
+
}
|
|
115
|
+
else if (p2 !== undefined) {
|
|
116
|
+
const { type } = accum[p2];
|
|
117
|
+
return type === 'tvar' ? '' : m;
|
|
118
|
+
}
|
|
119
|
+
return '';
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
plain.setText(expanded);
|
|
123
|
+
}
|
|
124
|
+
token.setText((0, string_1.removeCommentLine)(token.firstChild.toString(), true));
|
|
125
|
+
}
|
|
126
|
+
token.parseOnce();
|
|
127
|
+
for (const plain of [...accum.slice(n), token]) {
|
|
128
|
+
if (!plain || plain.length !== 1 || plain.firstChild.type !== 'text') {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const { data } = plain.firstChild;
|
|
132
|
+
if (!/\0\d+[tm!{}+~-]\x7F/u.test(data)) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const expanded = data.replace(/([^\x7F]?)\0(\d+)[tm!{}+~-]\x7F/gu, (m, prev, i) => {
|
|
136
|
+
const target = accum[i], { type, name, length, firstChild: f, childNodes } = target, isTemplate = type === 'template', args = childNodes.slice(1);
|
|
137
|
+
if (type === 'arg') {
|
|
138
|
+
const arg = (0, string_1.removeCommentLine)(f.toString()).trim();
|
|
139
|
+
if (/\0\d+[tm!{}+~-]\x7F/u.test(arg)) {
|
|
140
|
+
return m;
|
|
141
|
+
}
|
|
142
|
+
else if (!context || !context.hasArg(arg)) {
|
|
143
|
+
const effective = target.childNodes[1] ?? target;
|
|
144
|
+
clean(accum, length === 1 ? f : effective);
|
|
145
|
+
return prev + effective.toString();
|
|
146
|
+
}
|
|
147
|
+
clean(accum, context.getArg(arg).lastChild);
|
|
148
|
+
return prev + context.getValue(arg);
|
|
149
|
+
}
|
|
150
|
+
else if (isTemplate || name === 'int') {
|
|
151
|
+
if (context === false) {
|
|
152
|
+
return m;
|
|
153
|
+
}
|
|
154
|
+
const nameToken = isTemplate ? f : args[0].lastChild, key = (0, string_1.removeComment)(nameToken.toString()), fallback = isTemplate ? m : `${prev}⧼${key}⧽`, { title, valid } = index_1.default.normalizeTitle((isTemplate ? '' : 'MediaWiki:') + key, 10, include, config, { halfParsed: true, temporary: true, page });
|
|
155
|
+
if (!valid) {
|
|
156
|
+
clean(accum, nameToken);
|
|
157
|
+
if (isTemplate) {
|
|
158
|
+
clean(accum, args);
|
|
159
|
+
return prev + target.toString();
|
|
160
|
+
}
|
|
161
|
+
return fallback;
|
|
162
|
+
}
|
|
163
|
+
const dest = loadTemplate(title, config);
|
|
164
|
+
if (dest === false) {
|
|
165
|
+
if (!isTemplate) {
|
|
166
|
+
clean(accum, nameToken);
|
|
167
|
+
}
|
|
168
|
+
return fallback;
|
|
169
|
+
}
|
|
170
|
+
else if (stack.includes(dest)) {
|
|
171
|
+
return `${prev}<span class="error">Template loop detected: [[${dest}]]</span>`;
|
|
172
|
+
}
|
|
173
|
+
let template = index_1.default.templates.get(dest).replace(/\n$/u, '');
|
|
174
|
+
if (!isTemplate) {
|
|
175
|
+
for (let j = 1; j < args.length; j++) {
|
|
176
|
+
template = template.replaceAll(`$${j}`, (0, string_1.removeComment)(args[j].toString()));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return implicitNewLine(expand(template, dest, callPage, config, true, target, now, accum, [...stack, dest])
|
|
180
|
+
.toString(), prev);
|
|
181
|
+
}
|
|
182
|
+
else if (context === false && !solvedMagicWords.has(name)) {
|
|
183
|
+
return m;
|
|
184
|
+
}
|
|
185
|
+
else if (constants_1.functionHooks.has(name)) {
|
|
186
|
+
clean(accum, args);
|
|
187
|
+
return implicitNewLine(constants_1.functionHooks.get(name)(target, context || undefined), prev);
|
|
188
|
+
}
|
|
189
|
+
else if (magicWords_1.expandedMagicWords.has(name)) {
|
|
190
|
+
const result = (0, magicWords_1.expandMagicWord)(name, args.map(({ anon, name: key, value }) => anon ? value : `${key}=${value}`), callPage, config, now, accum);
|
|
191
|
+
if (result === false) {
|
|
192
|
+
return m;
|
|
193
|
+
}
|
|
194
|
+
clean(accum, args);
|
|
195
|
+
return implicitNewLine(result, prev);
|
|
196
|
+
}
|
|
197
|
+
return m;
|
|
198
|
+
});
|
|
199
|
+
plain.setText(expanded);
|
|
200
|
+
if (plain.type === 'parameter-key') {
|
|
201
|
+
plain.parentNode.trimName((0, string_1.removeCommentLine)(expanded));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return token;
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* 展开指定节点的模板
|
|
208
|
+
* @param token 目标节点
|
|
209
|
+
* @param context 模板调用环境
|
|
210
|
+
*/
|
|
211
|
+
const expandToken = (token, context) => {
|
|
212
|
+
const { pageName } = token;
|
|
213
|
+
return expand(token.toString(), pageName, pageName, token.getAttribute('config'), token.getAttribute('include'), context);
|
|
214
|
+
};
|
|
215
|
+
exports.expandToken = expandToken;
|
|
216
|
+
constants_1.parsers['expandToken'] = __filename;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint @stylistic/operator-linebreak: [2, "before", {overrides: {"=": "after"}}] */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.renderExt = void 0;
|
|
5
|
+
const constants_1 = require("../util/constants");
|
|
6
|
+
const string_1 = require("../util/string");
|
|
7
|
+
/**
|
|
8
|
+
* 将扩展标签渲染为HTML
|
|
9
|
+
* @param token 扩展标签节点
|
|
10
|
+
* @param opt 渲染选项
|
|
11
|
+
*/
|
|
12
|
+
const renderExt = (token, opt) => {
|
|
13
|
+
const { name, firstChild, lastChild } = token;
|
|
14
|
+
switch (name) {
|
|
15
|
+
case 'poem': {
|
|
16
|
+
const padding = firstChild.hasAttr('compact') ? '' : '\n';
|
|
17
|
+
firstChild.classList.add('poem');
|
|
18
|
+
return `<div${firstChild.toHtmlInternal()}>${padding}${lastChild.toHtmlInternal({ ...opt, nowrap: false })
|
|
19
|
+
.replace(/(?<!^|<hr>)\n(?!$)/gu, '<br>\n')
|
|
20
|
+
.replace(/^ +/gmu, p => ' '.repeat(p.length))
|
|
21
|
+
.trim()}${padding}</div>`;
|
|
22
|
+
}
|
|
23
|
+
case 'gallery': {
|
|
24
|
+
const caption = firstChild.getAttrToken('caption'), perrow = parseInt(String(firstChild.getAttr('perrow'))), mode = firstChild.getAttr('mode'), { classList } = firstChild, nolines = typeof mode === 'string' && mode.toLowerCase() === 'nolines', padding = nolines ? 9 : 43;
|
|
25
|
+
classList.add('gallery');
|
|
26
|
+
if (nolines) {
|
|
27
|
+
classList.add('mw-gallery-nolines');
|
|
28
|
+
}
|
|
29
|
+
if (perrow > 0) {
|
|
30
|
+
const style = firstChild.getAttr('style');
|
|
31
|
+
firstChild.setAttr('style', `max-width: ${(lastChild.widths + padding) * perrow}px;${typeof style === 'string' ? style : ''}`);
|
|
32
|
+
}
|
|
33
|
+
return `<ul${firstChild.toHtmlInternal()}>\n${caption
|
|
34
|
+
? `\t<li class="gallerycaption">${caption.lastChild.toHtmlInternal({ nowrap: true })}</li>\n`
|
|
35
|
+
: ''}${lastChild.toHtmlInternal()}\n</ul>`;
|
|
36
|
+
}
|
|
37
|
+
case 'syntaxhighlight':
|
|
38
|
+
case 'source': {
|
|
39
|
+
let html = lastChild.toHtmlInternal().trimEnd().replace(/^\n+/u, ''), lexer = firstChild.getAttr('lang');
|
|
40
|
+
const dir = firstChild.getAttr('dir') === 'rtl' ? ' rtl' : 'ltr', isInline = firstChild.getAttr('enclose') === 'none' || firstChild.hasAttr('inline'), showLines = firstChild.hasAttr('line'), { classList } = firstChild;
|
|
41
|
+
classList.add('mw-highlight');
|
|
42
|
+
if (lexer && lexer !== true) {
|
|
43
|
+
const { Prism, loadLanguage } = require('./syntaxhighlight');
|
|
44
|
+
lexer = lexer.toLowerCase();
|
|
45
|
+
if (Prism) {
|
|
46
|
+
try {
|
|
47
|
+
lexer = loadLanguage(lexer);
|
|
48
|
+
html = Prism.highlight(lastChild.childNodes.map(child => child.is('ext') && child.name === 'nowiki'
|
|
49
|
+
? child.innerText ?? ''
|
|
50
|
+
: child.toString()).join().trimEnd().replace(/^\n+/u, ''), Prism.languages[lexer], lexer);
|
|
51
|
+
classList.add(`mw-highlight-lang-${lexer.toLowerCase()}`);
|
|
52
|
+
}
|
|
53
|
+
catch { }
|
|
54
|
+
}
|
|
55
|
+
const highlight = firstChild.getAttr('highlight'), lines = typeof highlight === 'string' && new Set(highlight.split(',').flatMap((str) => {
|
|
56
|
+
const num = Number(str);
|
|
57
|
+
if (Number.isInteger(num) && num > 0) {
|
|
58
|
+
return num;
|
|
59
|
+
}
|
|
60
|
+
else if (!str.includes('-')) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
const [start, end] = str.split('-')
|
|
64
|
+
.map(s => parseInt(s));
|
|
65
|
+
return start > 0 && start < end
|
|
66
|
+
? Array.from({ length: end - start + 1 }, (_, i) => i + start)
|
|
67
|
+
: [];
|
|
68
|
+
})), linenos = showLines && !isInline;
|
|
69
|
+
if (linenos || lines && lines.size > 0) {
|
|
70
|
+
let lineReplace, begin = '', end = '', start = 1;
|
|
71
|
+
if (linenos) {
|
|
72
|
+
const linelinks = firstChild.getAttr('linelinks'), startAttr = firstChild.getAttr('start');
|
|
73
|
+
lineReplace = '<span class="linenos" data-line="$1"></span>';
|
|
74
|
+
if (startAttr && startAttr !== true) {
|
|
75
|
+
start = Number(startAttr);
|
|
76
|
+
if (!Number.isInteger(start) || start < 0) {
|
|
77
|
+
start = 1;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (linelinks && linelinks !== true) {
|
|
81
|
+
lineReplace = `<a href="#${linelinks}-$1">${lineReplace}</a>`;
|
|
82
|
+
begin = `${linelinks}-`;
|
|
83
|
+
end = '</span>';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const re = /<\/?span\b[^>]*>|\n/gu, stack = [],
|
|
87
|
+
/**
|
|
88
|
+
* 是否高亮
|
|
89
|
+
* @param i 行号
|
|
90
|
+
* @param close 闭合标签
|
|
91
|
+
*/
|
|
92
|
+
f = (i, close) => {
|
|
93
|
+
if (!lines || !lines.has(i)) {
|
|
94
|
+
return '';
|
|
95
|
+
}
|
|
96
|
+
return close ? '</span>' : '<span class="hll">';
|
|
97
|
+
},
|
|
98
|
+
/**
|
|
99
|
+
* 是否添加id属性
|
|
100
|
+
* @param i 行号
|
|
101
|
+
*/
|
|
102
|
+
g = (i) => begin && `<span id="${begin}${i}">`;
|
|
103
|
+
let mt = re.exec(html), i = 1, lastIndex = 0, output = g(i) + f(1) + (lineReplace?.replaceAll('$1', String(start)) ?? '');
|
|
104
|
+
while (mt) {
|
|
105
|
+
if (mt[0] === '\n') {
|
|
106
|
+
output += `${html.slice(lastIndex, mt.index)}${'</span>'.repeat(stack.length)}\n${f(i, true)}${end}${g(i + 1)}${f(i + 1)}${lineReplace?.replaceAll('$1', String(i + start)) ?? ''}${stack.join('')}`;
|
|
107
|
+
i++;
|
|
108
|
+
({ lastIndex } = re);
|
|
109
|
+
}
|
|
110
|
+
else if (mt[0].startsWith('</')) {
|
|
111
|
+
stack.pop();
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
stack.push(mt[0]);
|
|
115
|
+
}
|
|
116
|
+
mt = re.exec(html);
|
|
117
|
+
}
|
|
118
|
+
html = output + html.slice(lastIndex) + f(i, true) + end;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
classList.add(`mw-content-${dir}`);
|
|
122
|
+
if (showLines) {
|
|
123
|
+
classList.add('mw-highlight-lines');
|
|
124
|
+
}
|
|
125
|
+
if (!isInline && firstChild.hasAttr('copy')) {
|
|
126
|
+
classList.add('mw-highlight-copy');
|
|
127
|
+
}
|
|
128
|
+
firstChild.setAttr('dir', dir);
|
|
129
|
+
for (const attr of ['style', 'class', 'id', 'dir']) {
|
|
130
|
+
firstChild.getAttrToken(attr)?.asHtmlAttr();
|
|
131
|
+
}
|
|
132
|
+
return isInline
|
|
133
|
+
? `<code${firstChild.toHtmlInternal()}>${html.trim().replaceAll('\n', ' ')}</code>`
|
|
134
|
+
: `<div${firstChild.toHtmlInternal()}>${html && `<pre>${(0, string_1.newline)(html)}</pre>`}</div>`;
|
|
135
|
+
}
|
|
136
|
+
default:
|
|
137
|
+
return '';
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
exports.renderExt = renderExt;
|
|
141
|
+
constants_1.parsers['renderExt'] = __filename;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toHtml = void 0;
|
|
4
|
+
const constants_1 = require("../util/constants");
|
|
5
|
+
const blockElems = 'table|h1|h2|h3|h4|h5|h6|pre|p|ul|ol|dl', antiBlockElems = 'td|th';
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
7
|
+
/<(?:table|\/(?:td|th)|\/?(?:tr|caption|dt|dd|li))\b/iu;
|
|
8
|
+
const openRegex = new RegExp(String.raw `<(?:${blockElems}|\/(?:${antiBlockElems})|\/?(?:tr|caption|dt|dd|li))\b`, 'iu');
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
10
|
+
/<(?:\/(?:h1|h2)|td|th|\/?(?:center|blockquote|div|hr|figure))\b/iu;
|
|
11
|
+
const closeRegex = new RegExp(String.raw `<(?:\/(?:${blockElems})|${antiBlockElems}|\/?(?:center|blockquote|div|hr|figure))\b`, 'iu');
|
|
12
|
+
/**
|
|
13
|
+
* 将展开后的节点转换为HTML
|
|
14
|
+
* @param token 展开后的节点
|
|
15
|
+
*/
|
|
16
|
+
const toHtml = (token) => {
|
|
17
|
+
constants_1.states.set(token, { headings: new Set(), categories: new Set() });
|
|
18
|
+
const lines = token.toHtmlInternal().split('\n');
|
|
19
|
+
let output = '', inBlockElem = false, pendingPTag = false, inBlockquote = false, lastParagraph = '';
|
|
20
|
+
const /** @ignore */ closeParagraph = () => {
|
|
21
|
+
if (lastParagraph) {
|
|
22
|
+
const result = `</${lastParagraph}>\n`;
|
|
23
|
+
lastParagraph = '';
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
return '';
|
|
27
|
+
};
|
|
28
|
+
for (let line of lines) {
|
|
29
|
+
const openMatch = openRegex.test(line), closeMatch = closeRegex.test(line);
|
|
30
|
+
if (openMatch || closeMatch) {
|
|
31
|
+
const blockquote = /<(\/?)blockquote[\s>](?!.*<\/?blockquote[\s>])/iu.exec(line)?.[1];
|
|
32
|
+
inBlockquote = blockquote === undefined ? inBlockquote : !blockquote;
|
|
33
|
+
pendingPTag = false;
|
|
34
|
+
output += closeParagraph();
|
|
35
|
+
inBlockElem = !closeMatch;
|
|
36
|
+
}
|
|
37
|
+
else if (!inBlockElem) {
|
|
38
|
+
if (line.startsWith(' ') && (lastParagraph === 'pre' || line.trim()) && !inBlockquote) {
|
|
39
|
+
if (lastParagraph !== 'pre') {
|
|
40
|
+
pendingPTag = false;
|
|
41
|
+
output += `${closeParagraph()}<pre>`;
|
|
42
|
+
lastParagraph = 'pre';
|
|
43
|
+
}
|
|
44
|
+
line = line.slice(1);
|
|
45
|
+
}
|
|
46
|
+
else if (/^(?:<link\b[^>]*>\s*)+$/iu.test(line)) {
|
|
47
|
+
if (pendingPTag) {
|
|
48
|
+
output += closeParagraph();
|
|
49
|
+
pendingPTag = false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else if (!line.trim()) {
|
|
53
|
+
if (pendingPTag) {
|
|
54
|
+
output += `${pendingPTag}<br>`;
|
|
55
|
+
pendingPTag = false;
|
|
56
|
+
lastParagraph = 'p';
|
|
57
|
+
}
|
|
58
|
+
else if (lastParagraph === 'p') {
|
|
59
|
+
pendingPTag = '</p><p>';
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
output += closeParagraph();
|
|
63
|
+
pendingPTag = '<p>';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else if (pendingPTag) {
|
|
67
|
+
output += pendingPTag;
|
|
68
|
+
pendingPTag = false;
|
|
69
|
+
lastParagraph = 'p';
|
|
70
|
+
}
|
|
71
|
+
else if (lastParagraph !== 'p') {
|
|
72
|
+
output += `${closeParagraph()}<p>`;
|
|
73
|
+
lastParagraph = 'p';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (!pendingPTag) {
|
|
77
|
+
output += `${line}\n`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
output += closeParagraph();
|
|
81
|
+
const { categories } = constants_1.states.get(token);
|
|
82
|
+
constants_1.states.delete(token);
|
|
83
|
+
let html = output.trimEnd();
|
|
84
|
+
if (categories.size > 0) {
|
|
85
|
+
html += `
|
|
86
|
+
<div id="catlinks" class="catlinks"><div><a href="${token.normalizeTitle('Special:Categories', -1, { temporary: true }).getUrl()}" title="Special:Categories">Categories</a>: <ul>${[...categories].map(catlink => `<li>${catlink}</li>`).join('')}</div></div>`;
|
|
87
|
+
}
|
|
88
|
+
return html;
|
|
89
|
+
};
|
|
90
|
+
exports.toHtml = toHtml;
|
|
91
|
+
constants_1.parsers['toHtml'] = __filename;
|