wikiparser-node 1.29.2 → 1.31.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 (107) hide show
  1. package/README.md +8 -4
  2. package/bundle/bundle-es8.min.js +29 -26
  3. package/bundle/bundle-lsp.min.js +34 -31
  4. package/bundle/bundle.min.js +22 -22
  5. package/dist/addon/magicWords.js +642 -40
  6. package/dist/addon/token.js +99 -111
  7. package/dist/addon/transclude.js +2 -2
  8. package/dist/base.d.mts +4 -3
  9. package/dist/base.d.ts +4 -3
  10. package/dist/base.js +3 -0
  11. package/dist/base.mjs +3 -0
  12. package/dist/bin/config.js +1 -1
  13. package/dist/index.d.ts +11 -1
  14. package/dist/index.js +47 -15
  15. package/dist/internal.d.ts +2 -1
  16. package/dist/lib/attributes.js +0 -1
  17. package/dist/lib/document.js +2 -2
  18. package/dist/lib/element.d.ts +1 -1
  19. package/dist/lib/element.js +9 -7
  20. package/dist/lib/lintConfig.js +12 -1
  21. package/dist/lib/lsp.d.ts +0 -1
  22. package/dist/lib/lsp.js +2 -5
  23. package/dist/lib/node.js +7 -9
  24. package/dist/lib/redirectMap.js +1 -1
  25. package/dist/lib/text.js +68 -22
  26. package/dist/lib/title.js +1 -1
  27. package/dist/mixin/attributesParent.d.ts +0 -1
  28. package/dist/mixin/attributesParent.js +2 -4
  29. package/dist/mixin/elementLike.js +2 -4
  30. package/dist/mixin/fixed.js +0 -2
  31. package/dist/mixin/gapped.js +0 -2
  32. package/dist/mixin/hidden.js +1 -3
  33. package/dist/mixin/noEscape.js +1 -3
  34. package/dist/mixin/nodeLike.js +2 -4
  35. package/dist/mixin/padded.js +5 -9
  36. package/dist/mixin/singleLine.js +0 -2
  37. package/dist/mixin/sol.js +0 -2
  38. package/dist/mixin/syntax.js +0 -2
  39. package/dist/parser/commentAndExt.js +1 -1
  40. package/dist/parser/hrAndDoubleUnderscore.js +11 -9
  41. package/dist/parser/quotes.js +20 -8
  42. package/dist/parser/table.js +1 -2
  43. package/dist/src/arg.js +10 -5
  44. package/dist/src/atom.js +2 -0
  45. package/dist/src/attribute.js +12 -12
  46. package/dist/src/attributes.js +6 -5
  47. package/dist/src/commented.js +0 -1
  48. package/dist/src/converterFlags.js +1 -1
  49. package/dist/src/converterRule.js +1 -2
  50. package/dist/src/extLink.js +2 -2
  51. package/dist/src/heading.d.ts +8 -0
  52. package/dist/src/heading.js +25 -2
  53. package/dist/src/imageParameter.js +6 -7
  54. package/dist/src/index.d.ts +0 -2
  55. package/dist/src/index.js +5 -9
  56. package/dist/src/link/base.js +13 -6
  57. package/dist/src/link/category.d.ts +1 -1
  58. package/dist/src/link/category.js +5 -3
  59. package/dist/src/link/file.js +9 -9
  60. package/dist/src/link/galleryImage.d.ts +0 -2
  61. package/dist/src/link/galleryImage.js +2 -4
  62. package/dist/src/link/index.js +4 -4
  63. package/dist/src/link/redirectTarget.js +1 -1
  64. package/dist/src/magicLink.js +3 -3
  65. package/dist/src/multiLine/gallery.js +5 -5
  66. package/dist/src/multiLine/imagemap.js +1 -1
  67. package/dist/src/multiLine/paramTag.js +1 -1
  68. package/dist/src/nested.js +1 -1
  69. package/dist/src/nowiki/comment.js +1 -1
  70. package/dist/src/nowiki/dd.d.ts +3 -0
  71. package/dist/src/nowiki/doubleUnderscore.d.ts +3 -0
  72. package/dist/src/nowiki/hr.d.ts +3 -0
  73. package/dist/src/nowiki/index.js +3 -3
  74. package/dist/src/nowiki/list.d.ts +5 -0
  75. package/dist/src/nowiki/list.js +32 -0
  76. package/dist/src/nowiki/listBase.js +2 -2
  77. package/dist/src/nowiki/noinclude.d.ts +2 -1
  78. package/dist/src/nowiki/noinclude.js +26 -1
  79. package/dist/src/nowiki/quote.d.ts +21 -0
  80. package/dist/src/nowiki/quote.js +61 -4
  81. package/dist/src/paramLine.js +2 -0
  82. package/dist/src/parameter.js +3 -3
  83. package/dist/src/pre.js +1 -1
  84. package/dist/src/redirect.js +1 -1
  85. package/dist/src/syntax.js +1 -1
  86. package/dist/src/table/base.js +2 -3
  87. package/dist/src/table/index.d.ts +1 -5
  88. package/dist/src/table/index.js +8 -10
  89. package/dist/src/table/td.js +11 -12
  90. package/dist/src/table/trBase.js +2 -4
  91. package/dist/src/tag/html.js +4 -5
  92. package/dist/src/tag/index.js +9 -8
  93. package/dist/src/tagPair/ext.js +7 -6
  94. package/dist/src/tagPair/include.js +1 -1
  95. package/dist/src/tagPair/translate.d.ts +1 -0
  96. package/dist/src/tagPair/translate.js +8 -1
  97. package/dist/src/transclude.d.ts +6 -6
  98. package/dist/src/transclude.js +25 -18
  99. package/dist/util/constants.js +4 -2
  100. package/dist/util/html.js +11 -5
  101. package/dist/util/string.js +22 -10
  102. package/extensions/dist/base.js +3 -3
  103. package/extensions/editor.css +1 -1
  104. package/i18n/en.json +3 -0
  105. package/i18n/zh-hans.json +3 -0
  106. package/i18n/zh-hant.json +3 -0
  107. package/package.json +34 -29
@@ -8,6 +8,7 @@ const path_1 = __importDefault(require("path"));
8
8
  const constants_1 = require("../util/constants");
9
9
  const debug_1 = require("../util/debug");
10
10
  const string_1 = require("../util/string");
11
+ const redirect_1 = require("../parser/redirect");
11
12
  const index_1 = __importDefault(require("../index"));
12
13
  const index_2 = require("../src/index");
13
14
  const comment_1 = require("../src/nowiki/comment");
@@ -20,6 +21,7 @@ const blockElems = 'table|h1|h2|h3|h4|h5|h6|pre|p|ul|ol|dl', antiBlockElems = 't
20
21
  'if',
21
22
  'ifeq',
22
23
  'ifexist',
24
+ 'iferror',
23
25
  'switch',
24
26
  ]);
25
27
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
@@ -35,29 +37,46 @@ const closeRegex = new RegExp(String.raw `<(?:\/(?:${blockElems})|${antiBlockEle
35
37
  */
36
38
  const implicitNewLine = (str, prev) => prev + (prev !== '\n' && /^(?:\{\||[:;#*])/u.test(str) ? `\n${str}` : str);
37
39
  /**
38
- * 比较两个字符串是否相等
39
- * @param a
40
- * @param b
41
- */
42
- const cmp = (a, b) => a === b || Boolean(a && b) && Number(a) === Number(b);
43
- /**
44
- * 解析 if/ifexist/ifeq 解析器函数
45
- * @param accum
46
- * @param prev 解析器函数前的字符串
47
- * @param effective 生效的参数
40
+ * 加载模板
41
+ * @param title 模板名
42
+ * @param config
48
43
  */
49
- const parseIf = (accum, prev, effective) => {
50
- if (effective) {
51
- // @ts-expect-error sparse array
52
- accum[accum.indexOf(effective.lastChild)] = undefined;
53
- return implicitNewLine(effective.value, prev);
44
+ const loadTemplate = (title, config) => {
45
+ if (index_1.default.templates.has(title)) {
46
+ return title;
47
+ }
48
+ else if (index_1.default.templateDir === undefined) {
49
+ return false;
50
+ }
51
+ else if (!path_1.default.isAbsolute(index_1.default.templateDir)) {
52
+ index_1.default.templateDir = path_1.default.join(__dirname, '..', '..', index_1.default.templateDir);
53
+ }
54
+ const file = fs_1.default.readdirSync(index_1.default.templateDir, { withFileTypes: true, recursive: true })
55
+ .filter(dirent => dirent.isFile())
56
+ .find(({ name, parentPath }) => {
57
+ const t = path_1.default.relative(index_1.default.templateDir, path_1.default.join(parentPath, name.replace(/\.(?:wiki|txt)$/iu, ''))).replaceAll('꞉', ':');
58
+ try {
59
+ return decodeURIComponent(t) === title;
60
+ }
61
+ catch {
62
+ return t === title;
63
+ }
64
+ });
65
+ if (!file) {
66
+ return false;
54
67
  }
55
- return prev;
68
+ 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));
69
+ if (parsed) {
70
+ return loadTemplate(accum[0].lastChild.getTitle().title, config);
71
+ }
72
+ index_1.default.templates.set(title, content);
73
+ return title;
56
74
  };
57
75
  /**
58
76
  * 展开模板
59
77
  * @param wikitext
60
78
  * @param page 页面名称
79
+ * @param callPage 调用页面名称
61
80
  * @param config
62
81
  * @param include
63
82
  * @param context 模板调用环境
@@ -65,28 +84,63 @@ const parseIf = (accum, prev, effective) => {
65
84
  * @param accum
66
85
  * @param stack 模板调用栈
67
86
  */
68
- const expand = (wikitext, page, config, include, context, now = index_1.default.now ?? new Date(), accum = [], stack = []) => {
87
+ const expand = (wikitext, page, callPage, config, include, context, now = index_1.default.now, accum = [], stack = []) => {
69
88
  const n = accum.length, token = new index_2.Token(wikitext, { ...config, inExt: true }, accum);
70
89
  token.type = 'root';
71
90
  token.pageName = page;
72
91
  token.parseOnce(0, include);
73
92
  if (context !== false) {
93
+ for (const plain of [...accum.slice(n), token]) {
94
+ if (plain.length !== 1 || plain.firstChild.type !== 'text') {
95
+ continue;
96
+ }
97
+ const { data } = plain.firstChild;
98
+ if (!/\0\d+g\x7F/u.test(data)) {
99
+ continue;
100
+ }
101
+ const expanded = data.replace(/\0(\d+)g\x7F/gu, (_, i) => {
102
+ const target = accum[i];
103
+ if (target.type === 'onlyinclude') {
104
+ // @ts-expect-error sparse array
105
+ accum[accum.indexOf(target)] = undefined;
106
+ return target.firstChild.toString();
107
+ }
108
+ const { lastChild } = target;
109
+ // @ts-expect-error sparse array
110
+ accum[accum.indexOf(lastChild)] = undefined;
111
+ return lastChild.firstChild.toString().replace(/\0(\d+)c\x7F[\n ]|\0(\d+)n\x7F|^\n|\n$/gu, (m, p1, p2) => {
112
+ if (p1 !== undefined) {
113
+ const { innerText } = accum[p1];
114
+ return /^T:[^_/\n<>~]+$/u.test(innerText) ? '' : m;
115
+ }
116
+ else if (p2 !== undefined) {
117
+ const { type } = accum[p2];
118
+ return type === 'tvar' ? '' : m;
119
+ }
120
+ return '';
121
+ });
122
+ });
123
+ plain.setText(expanded);
124
+ if (plain.type === 'parameter-key') {
125
+ plain.parentNode.trimName((0, string_1.removeCommentLine)(expanded));
126
+ }
127
+ }
74
128
  token.setText((0, string_1.removeCommentLine)(token.firstChild.toString(), true));
75
129
  }
76
130
  token.parseOnce();
77
131
  for (const plain of [...accum.slice(n), token]) {
78
- if (plain.length !== 1 || plain.firstChild.type !== 'text') {
132
+ if (!plain || plain.length !== 1 || plain.firstChild.type !== 'text') {
79
133
  continue;
80
134
  }
81
135
  const { data } = plain.firstChild;
82
- if (!/\0\d+t\x7F/u.test(data)) {
136
+ if (!/\0\d+[tm]\x7F/u.test(data)) {
83
137
  continue;
84
138
  }
85
- const expanded = data.replace(/([^\x7F]?)\0(\d+)t\x7F/gu, (m, prev, i) => {
139
+ const expanded = data.replace(/([^\x7F]?)\0(\d+)[tm]\x7F/gu, (m, prev, i) => {
86
140
  const target = accum[i], { type, name, length, firstChild: f, childNodes } = target, isTemplate = type === 'template', args = childNodes.slice(1);
87
141
  if (type === 'arg') {
88
142
  const arg = (0, string_1.removeCommentLine)(f.toString()).trim();
89
- if (/\0\d+t\x7F/u.test(arg)) {
143
+ if (/\0\d+[tm]\x7F/u.test(arg)) {
90
144
  return m;
91
145
  }
92
146
  else if (!context || !context.hasArg(arg)) {
@@ -111,110 +165,40 @@ const expand = (wikitext, page, config, include, context, now = index_1.default.
111
165
  accum[accum.indexOf(f)] = undefined;
112
166
  return isTemplate ? prev + target.toString() : fallback;
113
167
  }
114
- else if (!index_1.default.templates.has(title)) {
115
- if (index_1.default.templateDir === undefined) {
116
- return fallback;
117
- }
118
- else if (!path_1.default.isAbsolute(index_1.default.templateDir)) {
119
- index_1.default.templateDir = path_1.default.join(__dirname, '..', '..', index_1.default.templateDir);
120
- }
121
- const file = fs_1.default.readdirSync(index_1.default.templateDir, { withFileTypes: true, recursive: true })
122
- .filter(dirent => dirent.isFile())
123
- .find(({ name: fl, parentPath }) => {
124
- const t = path_1.default.relative(index_1.default.templateDir, path_1.default.join(parentPath, fl.replace(/\.(?:wiki|txt)$/iu, ''))).replaceAll('꞉', ':');
125
- try {
126
- return decodeURIComponent(t) === title;
127
- }
128
- catch {
129
- return t === title;
130
- }
131
- });
132
- if (!file) {
133
- return fallback;
134
- }
135
- index_1.default.templates.set(title, fs_1.default.readFileSync(path_1.default.join(file.parentPath, file.name), 'utf8'));
168
+ const dest = loadTemplate(title, config);
169
+ if (dest === false) {
170
+ return fallback;
136
171
  }
137
- else if (stack.includes(title)) {
138
- return `${prev}<span class="error">Template loop detected: [[${title}]]</span>`;
172
+ else if (stack.includes(dest)) {
173
+ return `${prev}<span class="error">Template loop detected: [[${dest}]]</span>`;
139
174
  }
140
- let template = index_1.default.templates.get(title);
175
+ let template = index_1.default.templates.get(dest).replace(/\n$/u, '');
141
176
  if (!isTemplate) {
142
177
  for (let j = 1; j < args.length; j++) {
143
178
  template = template.replaceAll(`$${j}`, (0, string_1.removeComment)(args[j].toString()));
144
179
  }
145
180
  }
146
- return implicitNewLine(expand(template, title, config, true, target, now, accum, [...stack, title]).toString(), prev);
181
+ return implicitNewLine(expand(template, dest, callPage, config, true, target, now, accum, [...stack, dest])
182
+ .toString(), prev);
147
183
  }
148
184
  else if (index_1.default.functionHooks.has(name)) {
149
- return context === false ? m : index_1.default.functionHooks.get(name)(target, context || undefined);
185
+ return context === false
186
+ ? m
187
+ : implicitNewLine(index_1.default.functionHooks.get(name)(target, context || undefined), prev);
150
188
  }
151
189
  else if (magicWords_1.expandedMagicWords.has(name)) {
152
- return context === false ? m : `${prev}${(0, magicWords_1.expandMagicWord)(name, now, config, args)}`;
153
- }
154
- else if (!solvedMagicWords.has(name)) {
155
- return m;
156
- }
157
- else if (length < 3 || name === 'ifeq' && length === 3) {
158
- return prev;
159
- }
160
- const var1 = (0, string_1.decodeHtml)(args[0].value), var2 = (0, string_1.decodeHtml)(args[1].value), known = !/\0\d+t\x7F/u.test(var1);
161
- if (known && (name === 'if' || name === 'ifexist')) {
162
- let bool = Boolean(var1);
163
- if (name === 'ifexist') {
164
- const { valid, interwiki } = index_1.default.normalizeTitle(var1, 0, include, config, { halfParsed: true, temporary: true, page: '' });
165
- bool = valid && !interwiki;
190
+ const solved = solvedMagicWords.has(name);
191
+ if (context === false && !solved) {
192
+ return m;
166
193
  }
167
- return parseIf(accum, prev, args[bool ? 1 : 2]);
168
- }
169
- else if (known && name === 'ifeq' && !/\0\d+t\x7F/u.test(var2)) {
170
- return parseIf(accum, prev, args[cmp(var1, var2) ? 2 : 3]);
171
- }
172
- else if (known && name === 'switch') {
173
- let defaultVal = '', j = 2,
174
- /**
175
- * - `1` 表示默认值
176
- * - `2` 表示匹配值
177
- */
178
- found = 0, transclusion = false, defaultParam;
179
- for (; j < length; j++) {
180
- const { anon, value, lastChild, name: option } = args[j - 1];
181
- transclusion = /\0\d+t\x7F/u.test(anon ? value : option);
182
- if (anon) {
183
- if (j === length - 1) { // 位于最后的匿名参数是默认值
184
- defaultParam = lastChild;
185
- defaultVal = value;
186
- }
187
- else if (transclusion) { // 不支持复杂参数
188
- break;
189
- }
190
- else if (cmp(var1, (0, string_1.decodeHtml)(value))) { // 下一个命名参数视为匹配值
191
- found = 2;
192
- }
193
- else if (value === '#default' && found !== 2) { // 下一个命名参数视为默认值
194
- found = 1;
195
- }
196
- }
197
- else if (transclusion) { // 不支持复杂参数
198
- break;
199
- }
200
- else if (found === 2 || cmp(var1, (0, string_1.decodeHtml)(option))) { // 第一个匹配值
194
+ const result = (0, magicWords_1.expandMagicWord)(name, args.map(({ anon, name: key, value }) => anon ? value : `${key}=${value}`), callPage, config, now, accum);
195
+ if (solved && result !== false) {
196
+ for (const { lastChild } of args) {
201
197
  // @ts-expect-error sparse array
202
198
  accum[accum.indexOf(lastChild)] = undefined;
203
- return implicitNewLine(value, prev);
204
- }
205
- else if (found === 1 || option.toLowerCase() === '#default') { // 更新默认值
206
- defaultParam = lastChild;
207
- defaultVal = value;
208
- found = 0;
209
199
  }
210
200
  }
211
- if (j === length) { // 不含复杂参数
212
- if (defaultParam) {
213
- // @ts-expect-error sparse array
214
- accum[accum.indexOf(defaultParam)] = undefined;
215
- }
216
- return implicitNewLine(defaultVal, prev);
217
- }
201
+ return result === false ? m : implicitNewLine(result, prev);
218
202
  }
219
203
  return m;
220
204
  });
@@ -230,7 +214,10 @@ const expand = (wikitext, page, config, include, context, now = index_1.default.
230
214
  * @param token 目标节点
231
215
  * @param context 模板调用环境
232
216
  */
233
- const expandToken = (token, context) => expand(token.toString(), token.pageName, token.getAttribute('config'), token.getAttribute('include'), context);
217
+ const expandToken = (token, context) => {
218
+ const { pageName } = token;
219
+ return expand(token.toString(), pageName, pageName, token.getAttribute('config'), token.getAttribute('include'), context);
220
+ };
234
221
  index_2.Token.prototype.expand = /** @implements */ function () {
235
222
  return debug_1.Shadow.run(() => expandToken(this).parse());
236
223
  };
@@ -242,7 +229,8 @@ index_2.Token.prototype.toHtml = /** @implements */ function () {
242
229
  let html;
243
230
  if (this.type === 'root') {
244
231
  index_1.default.viewOnly = true;
245
- const expanded = debug_1.Shadow.run(() => expandToken(this).parse(undefined, false, true));
232
+ const expanded = debug_1.Shadow.run(() => expandToken(this).parse(undefined, false, true)), e = new Event('expand');
233
+ this.dispatchEvent(e, { type: 'expand', token: expanded });
246
234
  index_1.default.viewOnly = false;
247
235
  constants_1.states.set(expanded, { headings: new Set() });
248
236
  const lines = expanded.toHtmlInternal().split('\n');
@@ -17,8 +17,8 @@ const atom_1 = require("../src/atom");
17
17
  * @param token 魔术字或模板节点
18
18
  */
19
19
  const format = (token) => {
20
- const { lastChild, type } = token, isParameter = lastChild.type === 'parameter';
21
- if (!(type === 'template' ? isParameter && lastChild.anon : lastChild.type === 'magic-word-name')
20
+ const { lastChild, type } = token, isParameter = lastChild.is('parameter');
21
+ if (!(type === 'template' ? isParameter && lastChild.anon : lastChild.is('magic-word-name'))
22
22
  && !lastChild.toString().endsWith('\n')) {
23
23
  (isParameter ? lastChild.lastChild : lastChild).insertAt('\n');
24
24
  }
package/dist/base.d.mts CHANGED
@@ -18,7 +18,7 @@ export interface Config {
18
18
  readonly redirects?: [string, string][];
19
19
  }
20
20
  export type ConfigData = Omit<Config, 'excludes'>;
21
- export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | 'translate' | 'translate-attr' | 'translate-inner' | 'tvar' | 'tvar-name' | 'onlyinclude' | 'noinclude' | 'include' | 'comment' | 'ext' | 'ext-attrs' | 'ext-attr-dirty' | 'ext-attr' | 'attr-key' | 'attr-value' | 'ext-inner' | 'arg' | 'arg-name' | 'arg-default' | 'hidden' | 'magic-word' | 'magic-word-name' | 'invoke-function' | 'invoke-module' | 'template' | 'template-name' | 'parameter' | 'parameter-key' | 'parameter-value' | 'heading' | 'heading-title' | 'heading-trail' | 'html' | 'html-attrs' | 'html-attr-dirty' | 'html-attr' | 'table' | 'tr' | 'td' | 'table-syntax' | 'table-attrs' | 'table-attr-dirty' | 'table-attr' | 'table-inter' | 'td-inner' | 'hr' | 'double-underscore' | 'link' | 'link-target' | 'link-text' | 'category' | 'file' | 'gallery-image' | 'imagemap-image' | 'image-parameter' | 'quote' | 'ext-link' | 'ext-link-text' | 'ext-link-url' | 'free-ext-link' | 'magic-link' | 'list' | 'dd' | 'list-range' | 'converter' | 'converter-flags' | 'converter-flag' | 'converter-rule' | 'converter-rule-variant' | 'converter-rule-to' | 'converter-rule-from' | 'param-line' | 'imagemap-link';
21
+ export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | 'translate' | 'translate-attr' | 'translate-inner' | 'tvar' | 'tvar-name' | 'onlyinclude' | 'noinclude' | 'include' | 'comment' | 'ext' | 'ext-attrs' | 'ext-attr-dirty' | 'ext-attr' | 'attr-key' | 'attr-value' | 'ext-inner' | 'arg' | 'arg-name' | 'arg-default' | 'hidden' | 'magic-word' | 'magic-word-name' | 'invoke-function' | 'invoke-module' | 'template' | 'template-name' | 'parameter' | 'parameter-key' | 'parameter-value' | 'heading' | 'heading-title' | 'heading-trail' | 'html' | 'html-attrs' | 'html-attr-dirty' | 'html-attr' | 'table' | 'tr' | 'td' | 'table-syntax' | 'table-attrs' | 'table-attr-dirty' | 'table-attr' | 'table-inter' | 'td-inner' | 'hr' | 'double-underscore' | 'link' | 'link-target' | 'link-text' | 'category' | 'file' | 'gallery-image' | 'imagemap-image' | 'image-parameter' | 'quote' | 'ext-link' | 'ext-link-text' | 'ext-link-url' | 'free-ext-link' | 'magic-link' | 'list' | 'dd' | 'converter' | 'converter-flags' | 'converter-flag' | 'converter-rule' | 'converter-rule-variant' | 'converter-rule-to' | 'converter-rule-from' | 'param-line' | 'imagemap-link' | 'list-range';
22
22
  export declare const stages: {
23
23
  redirect: number;
24
24
  onlyinclude: number;
@@ -47,7 +47,7 @@ export declare const stages: {
47
47
  'list-range': number;
48
48
  };
49
49
  export type Stage = keyof typeof stages;
50
- export declare const rules: readonly ["bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "invalid-url", "lonely-apos", "lonely-bracket", "lonely-http", "nested-link", "no-arg", "no-duplicate", "no-ignored", "obsolete-attr", "obsolete-tag", "parsing-order", "pipe-like", "table-layout", "tag-like", "unbalanced-header", "unclosed-comment", "unclosed-quote", "unclosed-table", "unescaped", "unknown-page", "unmatched-tag", "unterminated-url", "url-encoding", "var-anchor", "void-ext", "invalid-css", "invalid-math"];
50
+ export declare const rules: readonly ["arg-in-ext", "bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "invalid-url", "lonely-apos", "lonely-bracket", "lonely-http", "nested-link", "no-arg", "no-duplicate", "no-ignored", "obsolete-attr", "obsolete-tag", "parsing-order", "pipe-like", "syntax-like", "table-layout", "tag-like", "unbalanced-header", "unclosed-comment", "unclosed-quote", "unclosed-table", "unescaped", "unknown-page", "unmatched-tag", "unterminated-url", "url-encoding", "var-anchor", "void-ext", "invalid-css", "invalid-math"];
51
51
  export declare namespace LintError {
52
52
  type Severity = 'error' | 'warning';
53
53
  type Rule = typeof rules[number];
@@ -313,7 +313,8 @@ export interface LintConfiguration extends FullLintConfig {
313
313
  getSeverity(rule: LintError.Rule, key?: string): LintError.Severity | false;
314
314
  }
315
315
  export interface Parser {
316
- config: ConfigData | string;
316
+ config: string | // eslint-disable-line @stylistic/operator-linebreak
317
+ ConfigData;
317
318
  i18n: Record<string, string> | string | undefined;
318
319
  /** @since v1.22.0 */
319
320
  lintConfig: LintConfig;
package/dist/base.d.ts CHANGED
@@ -18,7 +18,7 @@ export interface Config {
18
18
  readonly redirects?: [string, string][];
19
19
  }
20
20
  export type ConfigData = Omit<Config, 'excludes'>;
21
- export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | 'translate' | 'translate-attr' | 'translate-inner' | 'tvar' | 'tvar-name' | 'onlyinclude' | 'noinclude' | 'include' | 'comment' | 'ext' | 'ext-attrs' | 'ext-attr-dirty' | 'ext-attr' | 'attr-key' | 'attr-value' | 'ext-inner' | 'arg' | 'arg-name' | 'arg-default' | 'hidden' | 'magic-word' | 'magic-word-name' | 'invoke-function' | 'invoke-module' | 'template' | 'template-name' | 'parameter' | 'parameter-key' | 'parameter-value' | 'heading' | 'heading-title' | 'heading-trail' | 'html' | 'html-attrs' | 'html-attr-dirty' | 'html-attr' | 'table' | 'tr' | 'td' | 'table-syntax' | 'table-attrs' | 'table-attr-dirty' | 'table-attr' | 'table-inter' | 'td-inner' | 'hr' | 'double-underscore' | 'link' | 'link-target' | 'link-text' | 'category' | 'file' | 'gallery-image' | 'imagemap-image' | 'image-parameter' | 'quote' | 'ext-link' | 'ext-link-text' | 'ext-link-url' | 'free-ext-link' | 'magic-link' | 'list' | 'dd' | 'list-range' | 'converter' | 'converter-flags' | 'converter-flag' | 'converter-rule' | 'converter-rule-variant' | 'converter-rule-to' | 'converter-rule-from' | 'param-line' | 'imagemap-link';
21
+ export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | 'translate' | 'translate-attr' | 'translate-inner' | 'tvar' | 'tvar-name' | 'onlyinclude' | 'noinclude' | 'include' | 'comment' | 'ext' | 'ext-attrs' | 'ext-attr-dirty' | 'ext-attr' | 'attr-key' | 'attr-value' | 'ext-inner' | 'arg' | 'arg-name' | 'arg-default' | 'hidden' | 'magic-word' | 'magic-word-name' | 'invoke-function' | 'invoke-module' | 'template' | 'template-name' | 'parameter' | 'parameter-key' | 'parameter-value' | 'heading' | 'heading-title' | 'heading-trail' | 'html' | 'html-attrs' | 'html-attr-dirty' | 'html-attr' | 'table' | 'tr' | 'td' | 'table-syntax' | 'table-attrs' | 'table-attr-dirty' | 'table-attr' | 'table-inter' | 'td-inner' | 'hr' | 'double-underscore' | 'link' | 'link-target' | 'link-text' | 'category' | 'file' | 'gallery-image' | 'imagemap-image' | 'image-parameter' | 'quote' | 'ext-link' | 'ext-link-text' | 'ext-link-url' | 'free-ext-link' | 'magic-link' | 'list' | 'dd' | 'converter' | 'converter-flags' | 'converter-flag' | 'converter-rule' | 'converter-rule-variant' | 'converter-rule-to' | 'converter-rule-from' | 'param-line' | 'imagemap-link' | 'list-range';
22
22
  export declare const stages: {
23
23
  redirect: number;
24
24
  onlyinclude: number;
@@ -47,7 +47,7 @@ export declare const stages: {
47
47
  'list-range': number;
48
48
  };
49
49
  export type Stage = keyof typeof stages;
50
- export declare const rules: readonly ["bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "invalid-url", "lonely-apos", "lonely-bracket", "lonely-http", "nested-link", "no-arg", "no-duplicate", "no-ignored", "obsolete-attr", "obsolete-tag", "parsing-order", "pipe-like", "table-layout", "tag-like", "unbalanced-header", "unclosed-comment", "unclosed-quote", "unclosed-table", "unescaped", "unknown-page", "unmatched-tag", "unterminated-url", "url-encoding", "var-anchor", "void-ext", "invalid-css", "invalid-math"];
50
+ export declare const rules: readonly ["arg-in-ext", "bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "invalid-url", "lonely-apos", "lonely-bracket", "lonely-http", "nested-link", "no-arg", "no-duplicate", "no-ignored", "obsolete-attr", "obsolete-tag", "parsing-order", "pipe-like", "syntax-like", "table-layout", "tag-like", "unbalanced-header", "unclosed-comment", "unclosed-quote", "unclosed-table", "unescaped", "unknown-page", "unmatched-tag", "unterminated-url", "url-encoding", "var-anchor", "void-ext", "invalid-css", "invalid-math"];
51
51
  export declare namespace LintError {
52
52
  type Severity = 'error' | 'warning';
53
53
  type Rule = typeof rules[number];
@@ -313,7 +313,8 @@ export interface LintConfiguration extends FullLintConfig {
313
313
  getSeverity(rule: LintError.Rule, key?: string): LintError.Severity | false;
314
314
  }
315
315
  export interface Parser {
316
- config: ConfigData | string;
316
+ config: string | // eslint-disable-line @stylistic/operator-linebreak
317
+ ConfigData;
317
318
  i18n: Record<string, string> | string | undefined;
318
319
  /** @since v1.22.0 */
319
320
  lintConfig: LintConfig;
package/dist/base.js CHANGED
@@ -27,6 +27,7 @@ exports.stages = (() => {
27
27
  list: 10,
28
28
  dd: 10,
29
29
  converter: 11,
30
+ /* NOT FOR BROWSER */
30
31
  'list-range': 11,
31
32
  };
32
33
  Object.setPrototypeOf(obj, null);
@@ -34,6 +35,7 @@ exports.stages = (() => {
34
35
  })();
35
36
  exports.rules = (() => {
36
37
  const arr = [
38
+ 'arg-in-ext',
37
39
  'bold-header',
38
40
  'format-leakage',
39
41
  'fostered-content',
@@ -56,6 +58,7 @@ exports.rules = (() => {
56
58
  'obsolete-tag',
57
59
  'parsing-order',
58
60
  'pipe-like',
61
+ 'syntax-like',
59
62
  'table-layout',
60
63
  'tag-like',
61
64
  'unbalanced-header',
package/dist/base.mjs CHANGED
@@ -24,6 +24,7 @@ const stages = /* @__PURE__ */ (() => {
24
24
  list: 10,
25
25
  dd: 10,
26
26
  converter: 11,
27
+ /* NOT FOR BROWSER */
27
28
  "list-range": 11
28
29
  };
29
30
  Object.setPrototypeOf(obj, null);
@@ -31,6 +32,7 @@ const stages = /* @__PURE__ */ (() => {
31
32
  })();
32
33
  const rules = /* @__PURE__ */ (() => {
33
34
  const arr = [
35
+ "arg-in-ext",
34
36
  "bold-header",
35
37
  "format-leakage",
36
38
  "fostered-content",
@@ -53,6 +55,7 @@ const rules = /* @__PURE__ */ (() => {
53
55
  "obsolete-tag",
54
56
  "parsing-order",
55
57
  "pipe-like",
58
+ "syntax-like",
56
59
  "table-layout",
57
60
  "tag-like",
58
61
  "unbalanced-header",
@@ -77,7 +77,7 @@ const mw = {
77
77
  },
78
78
  },
79
79
  };
80
- const pkg = "wikiparser-node", version = "1.29.2";
80
+ const pkg = "wikiparser-node", version = "1.31.0";
81
81
  let mwConfig;
82
82
  /**
83
83
  * Get the parser configuration for a Wikimedia Foundation project.
package/dist/index.d.ts CHANGED
@@ -24,7 +24,7 @@ declare interface Parser extends ParserBase {
24
24
  * 指定解析器的当前时间
25
25
  * @since v1.21.2
26
26
  */
27
- now?: Date;
27
+ now: Date;
28
28
  configPaths: string[];
29
29
  /**
30
30
  * Normalize page title
@@ -81,6 +81,16 @@ declare interface Parser extends ParserBase {
81
81
  * @since v1.22.0
82
82
  */
83
83
  setHook(name: string, hook: TagHook): void;
84
+ /**
85
+ * Call a parser function
86
+ *
87
+ * 调用一个解析器函数
88
+ * @param name parser function name / 解析器函数名
89
+ * @param args arguments / 参数
90
+ * @since v1.30.1
91
+ */
92
+ callParserFunction(name: string, ...args: string[]): string;
93
+ callParserFunction(name: string, args: string[] | Record<string, string>): string;
84
94
  /**
85
95
  * Check if the title is an interwiki link
86
96
  *
package/dist/index.js CHANGED
@@ -34,37 +34,37 @@ let viewOnly = false;
34
34
  const promises = [Promise.resolve()];
35
35
  /^(zh|en)\s*:/diu; // eslint-disable-line @typescript-eslint/no-unused-expressions
36
36
  const getInterwikiRegex = (0, common_1.getRegex)(interwiki => new RegExp(String.raw `^(${interwiki.join('|')})\s*:`, 'diu'));
37
- let redirectMap = new redirectMap_1.RedirectMap();
37
+ let redirectMap = new redirectMap_1.RedirectMap(), now;
38
38
  /* NOT FOR BROWSER END */
39
39
  let lintConfig = (() => {
40
- LINT: return new lintConfig_1.LintConfiguration(); // eslint-disable-line no-unused-labels
40
+ LINT: return new lintConfig_1.LintConfiguration();
41
41
  })(), i18n;
42
42
  const Parser = {
43
43
  config: 'default',
44
44
  /** @implements */
45
45
  get rules() {
46
- LINT: return base_1.rules; // eslint-disable-line no-unused-labels
46
+ LINT: return base_1.rules;
47
47
  },
48
48
  /** @implements */
49
49
  get i18n() {
50
- LINT: return { ...constants_1.enMsg, ...i18n }; // eslint-disable-line no-unused-labels
50
+ LINT: return { ...constants_1.enMsg, ...i18n };
51
51
  },
52
52
  set i18n(data) {
53
53
  /* NOT FOR BROWSER ONLY */
54
- if (typeof data === 'string') { // eslint-disable-line unicorn/prefer-ternary
54
+ if (typeof data === 'string') {
55
55
  i18n = rootRequire(data, 'i18n');
56
56
  }
57
57
  else {
58
58
  /* NOT FOR BROWSER ONLY END */
59
- LINT: i18n = data; // eslint-disable-line no-unused-labels
59
+ LINT: i18n = data;
60
60
  }
61
61
  },
62
62
  /** @implements */
63
63
  get lintConfig() {
64
- LINT: return lintConfig; // eslint-disable-line no-unused-labels
64
+ LINT: return lintConfig;
65
65
  },
66
66
  set lintConfig(config) {
67
- LINT: lintConfig = new lintConfig_1.LintConfiguration(config); // eslint-disable-line no-unused-labels
67
+ LINT: lintConfig = new lintConfig_1.LintConfiguration(config);
68
68
  },
69
69
  /** @implements */
70
70
  get viewOnly() {
@@ -84,6 +84,13 @@ const Parser = {
84
84
  functionHooks: new Map(),
85
85
  tagHooks: new Map(),
86
86
  /** @implements */
87
+ get now() {
88
+ return now ?? new Date();
89
+ },
90
+ set now(value) {
91
+ now = value;
92
+ },
93
+ /** @implements */
87
94
  get redirects() {
88
95
  return redirectMap;
89
96
  },
@@ -152,7 +159,7 @@ const Parser = {
152
159
  },
153
160
  /** @implements */
154
161
  msg(msg, arg = '') {
155
- LINT: return msg // eslint-disable-line no-unused-labels
162
+ LINT: return msg
156
163
  && (this.i18n[msg] ?? msg).replace('$1', this.msg(arg));
157
164
  },
158
165
  /** @implements */
@@ -169,7 +176,7 @@ const Parser = {
169
176
  root.type = 'root';
170
177
  root.pageName = opt?.page;
171
178
  root.parseOnce(0, include).parseOnce();
172
- const t = new Title(root.toString(), defaultNs, config, opt);
179
+ const t = new Title(root.firstChild.toString(), defaultNs, config, opt);
173
180
  root.build();
174
181
  for (const key of ['main', 'fragment']) {
175
182
  const str = t[key];
@@ -211,7 +218,7 @@ const Parser = {
211
218
  maxStage ??= constants_1.MAX_STAGE;
212
219
  config ??= this.getConfig();
213
220
  let types;
214
- LINT: { // eslint-disable-line no-unused-labels
221
+ LINT: {
215
222
  if (typeof maxStage !== 'number') {
216
223
  types = Array.isArray(maxStage) ? maxStage : [maxStage];
217
224
  maxStage = Math.max(...types.map(t => base_1.stages[t] || constants_1.MAX_STAGE));
@@ -229,7 +236,7 @@ const Parser = {
229
236
  catch (e) /* istanbul ignore next */ {
230
237
  if (e instanceof Error) {
231
238
  const file = path_1.default.join(__dirname, '..', 'errors', new Date().toISOString()), stage = token.getAttribute('stage');
232
- for (const k in config) {
239
+ for (const k of Object.keys(config)) {
233
240
  if (k.startsWith('regex') || config[k] instanceof Set) {
234
241
  delete config[k];
235
242
  }
@@ -272,7 +279,7 @@ const Parser = {
272
279
  },
273
280
  /** @implements */
274
281
  async partialParse(wikitext, watch, include, config = Parser.getConfig()) {
275
- LSP: { // eslint-disable-line no-unused-labels
282
+ LSP: {
276
283
  const { Token } = require('./src/index');
277
284
  const set = typeof setImmediate === 'function' ? setImmediate : /* istanbul ignore next */ setTimeout, { running } = debug_1.Shadow;
278
285
  debug_1.Shadow.running = true;
@@ -313,7 +320,7 @@ const Parser = {
313
320
  },
314
321
  /** @implements */
315
322
  createLanguageService(uri = {}) {
316
- LSP: { // eslint-disable-line no-unused-labels
323
+ LSP: {
317
324
  const mod = require('./lib/lsp');
318
325
  const { LanguageService, tasks } = mod;
319
326
  this.viewOnly = true;
@@ -347,6 +354,31 @@ const Parser = {
347
354
  this.tagHooks.set(name, hook);
348
355
  },
349
356
  /** @implements */
357
+ callParserFunction(name, arg, ...args) {
358
+ const { expandMagicWord } = require('./addon/magicWords'), { getCanonicalName } = require('./src/transclude');
359
+ if (typeof arg === 'string') {
360
+ args.unshift(arg);
361
+ }
362
+ else if (Array.isArray(arg)) {
363
+ args = arg;
364
+ }
365
+ else if (arg) {
366
+ for (let i = 1; i in arg; i++) {
367
+ args.push(arg[i]);
368
+ delete arg[i];
369
+ }
370
+ for (const [key, value] of Object.entries(arg)) {
371
+ args.push(`${key}=${value}`);
372
+ }
373
+ }
374
+ const [, , , canonicalName] = getCanonicalName(name, this.getConfig().parserFunction), result = expandMagicWord((canonicalName || name.toLowerCase()), args);
375
+ /* istanbul ignore if */
376
+ if (result === false) {
377
+ throw new RangeError(`Unable to resolve parser function: ${name}`);
378
+ }
379
+ return result;
380
+ },
381
+ /** @implements */
350
382
  warn(msg, ...args) {
351
383
  /* istanbul ignore if */
352
384
  if (this.warning) {
@@ -453,7 +485,7 @@ const def = {
453
485
  'debugging',
454
486
  'isInterwiki',
455
487
  ]);
456
- for (const key in Parser) {
488
+ for (const key of Object.keys(Parser)) {
457
489
  if (!enumerable.has(key)) {
458
490
  def[key] = { enumerable: false };
459
491
  }
@@ -31,7 +31,6 @@ export type { GalleryImageToken } from './src/link/galleryImage';
31
31
  export type { QuoteToken } from './src/nowiki/quote';
32
32
  export type { MagicLinkToken } from './src/magicLink';
33
33
  export type { ExtLinkToken } from './src/extLink';
34
- export type { ListRangeToken } from './src/nowiki/listBase';
35
34
  export type { DdToken } from './src/nowiki/dd';
36
35
  export type { ListToken } from './src/nowiki/list';
37
36
  export type { ConverterFlagsToken } from './src/converterFlags';
@@ -47,3 +46,5 @@ export type { ImagemapLinkToken } from './src/imagemapLink';
47
46
  export type { ImagemapToken } from './src/multiLine/imagemap';
48
47
  export type { CommentedToken } from './src/commented';
49
48
  export type { TranslateToken } from './src/tagPair/translate';
49
+ export type { TvarToken } from './src/tag/tvar';
50
+ export type { ListRangeToken } from './src/nowiki/listBase';
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Attributes = void 0;
4
- /* eslint-disable jsdoc/require-jsdoc */
5
4
  const constants_1 = require("../util/constants");
6
5
  /** 用于选择器的属性 */
7
6
  class Attributes {