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.
Files changed (101) hide show
  1. package/README.md +9 -4
  2. package/bundle/bundle-es8.min.js +28 -29
  3. package/bundle/bundle-lsp.min.js +34 -34
  4. package/bundle/bundle.min.js +23 -23
  5. package/dist/addon/attribute.js +166 -0
  6. package/dist/addon/link.js +92 -0
  7. package/dist/addon/table.js +12 -3
  8. package/dist/addon/token.js +5 -316
  9. package/dist/addon/transclude.js +11 -8
  10. package/dist/base.d.mts +13 -4
  11. package/dist/base.d.ts +13 -4
  12. package/dist/base.js +2 -0
  13. package/dist/base.mjs +2 -0
  14. package/dist/bin/config.js +1 -1
  15. package/dist/index.d.ts +34 -5
  16. package/dist/index.js +100 -88
  17. package/dist/internal.d.ts +6 -1
  18. package/dist/lib/document.d.ts +9 -7
  19. package/dist/lib/document.js +91 -66
  20. package/dist/lib/element.d.ts +8 -8
  21. package/dist/lib/element.js +52 -50
  22. package/dist/lib/lintConfig.js +6 -2
  23. package/dist/lib/lsp.js +120 -56
  24. package/dist/lib/node.js +21 -15
  25. package/dist/lib/redirectMap.js +1 -1
  26. package/dist/lib/text.js +17 -14
  27. package/dist/lib/title.d.ts +11 -0
  28. package/dist/lib/title.js +37 -13
  29. package/dist/mixin/elementLike.js +2 -3
  30. package/dist/mixin/syntax.js +13 -7
  31. package/dist/parser/commentAndExt.js +4 -4
  32. package/dist/parser/hrAndDoubleUnderscore.js +11 -9
  33. package/dist/parser/selector.js +16 -41
  34. package/dist/render/expand.js +216 -0
  35. package/dist/render/extension.js +141 -0
  36. package/dist/render/html.js +91 -0
  37. package/dist/render/magicWords.js +768 -0
  38. package/dist/render/syntaxhighlight.js +415 -0
  39. package/dist/src/arg.js +8 -1
  40. package/dist/src/atom.js +2 -0
  41. package/dist/src/attribute.d.ts +5 -0
  42. package/dist/src/attribute.js +59 -104
  43. package/dist/src/attributes.d.ts +5 -0
  44. package/dist/src/attributes.js +17 -51
  45. package/dist/src/converter.js +4 -2
  46. package/dist/src/converterFlags.js +8 -6
  47. package/dist/src/converterRule.js +19 -15
  48. package/dist/src/extLink.js +2 -2
  49. package/dist/src/heading.d.ts +1 -1
  50. package/dist/src/heading.js +5 -3
  51. package/dist/src/imageParameter.d.ts +0 -1
  52. package/dist/src/imageParameter.js +34 -24
  53. package/dist/src/index.d.ts +5 -5
  54. package/dist/src/index.js +37 -27
  55. package/dist/src/link/base.js +26 -36
  56. package/dist/src/link/category.d.ts +8 -1
  57. package/dist/src/link/category.js +104 -38
  58. package/dist/src/link/file.js +21 -8
  59. package/dist/src/link/galleryImage.js +2 -3
  60. package/dist/src/link/index.d.ts +1 -1
  61. package/dist/src/link/index.js +12 -33
  62. package/dist/src/multiLine/index.js +1 -1
  63. package/dist/src/nowiki/comment.js +1 -1
  64. package/dist/src/nowiki/doubleUnderscore.js +4 -2
  65. package/dist/src/nowiki/index.js +34 -31
  66. package/dist/src/nowiki/list.js +3 -1
  67. package/dist/src/nowiki/listBase.d.ts +2 -1
  68. package/dist/src/nowiki/listBase.js +22 -8
  69. package/dist/src/nowiki/quote.d.ts +1 -1
  70. package/dist/src/nowiki/quote.js +3 -3
  71. package/dist/src/onlyinclude.js +1 -1
  72. package/dist/src/paramLine.js +2 -0
  73. package/dist/src/parameter.js +1 -1
  74. package/dist/src/redirect.js +1 -1
  75. package/dist/src/table/base.js +5 -4
  76. package/dist/src/table/index.d.ts +1 -6
  77. package/dist/src/table/index.js +7 -16
  78. package/dist/src/table/td.js +12 -9
  79. package/dist/src/tag/html.js +2 -3
  80. package/dist/src/tag/index.js +8 -6
  81. package/dist/src/tagPair/ext.js +18 -41
  82. package/dist/src/tagPair/index.js +6 -4
  83. package/dist/src/tagPair/translate.d.ts +1 -0
  84. package/dist/src/tagPair/translate.js +10 -3
  85. package/dist/src/transclude.js +18 -21
  86. package/dist/util/constants.js +9 -3
  87. package/dist/util/debug.js +72 -5
  88. package/dist/util/diff.js +17 -15
  89. package/dist/util/html.js +14 -5
  90. package/dist/util/selector.js +37 -0
  91. package/dist/util/sharable.d.mts +4 -1
  92. package/dist/util/sharable.js +7 -7
  93. package/dist/util/sharable.mjs +7 -7
  94. package/dist/util/string.js +51 -20
  95. package/extensions/dist/base.js +2 -2
  96. package/extensions/editor.css +1 -1
  97. package/i18n/en.json +1 -0
  98. package/i18n/zh-hans.json +30 -29
  99. package/i18n/zh-hant.json +31 -30
  100. package/package.json +23 -16
  101. 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}e\x7F`;
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 translate_1.TranslateToken(p1, p2 && (0, string_1.restore)(p2, stack), newConfig, accum);
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
- /__(toc|notoc)__|_{2}(目次)_{2}/giu; // eslint-disable-line @typescript-eslint/no-unused-expressions
22
- config.regexHrAndDoubleUnderscore ??= new RegExp(`__(${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}`, 'giu');
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(/^((?:\0\d+[cno]\x7F)*)(-{4,})/gmu, (_, lead, m) => {
27
- // @ts-expect-error abstract class
28
- new hr_1.HrToken(m, config, accum);
29
- return `${lead}\0${accum.length - 1}r\x7F`;
30
- }).replace(config.regexHrAndDoubleUnderscore, (m, p1, p2) => {
31
- const key = p1 ?? p2, caseSensitive = config.sensitiveDoubleUnderscore.has(key), lc = key.toLowerCase(), caseInsensitive = config.insensitiveDoubleUnderscore.has(lc);
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(p2), config, accum);
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;
@@ -1,24 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getCondition = void 0;
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, exports.getCondition)(s, scope)(token);
202
+ return (0, selector_1.getCondition)(s, scope)(token);
216
203
  case 'not':
217
- return !(0, exports.getCondition)(s, scope)(token);
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, exports.getCondition)(s, scope, token), childOrSibling = attributes.siblings && /(?:^|,)\s*[+~]/u.test(s)
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
- for (const [c, entity] of specialChars) {
312
- selector = selector.replaceAll(entity, c);
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
- for (const [c, entity] of specialChars) {
330
- sanitized = sanitized.replaceAll(`\\${c}`, entity);
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
- /* NOT FOR BROWSER END */
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 => '&nbsp;'.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;