wikiparser-node 1.18.3 → 1.19.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.
@@ -143,42 +143,50 @@ const expand = (wikitext, config, include, context, accum = [], stack = []) => {
143
143
  return parseIf(accum, prev, c[cmp(var1, var2) ? 3 : 4]);
144
144
  }
145
145
  else if (known && name === 'switch') {
146
- let defaultVal = '', found = false, transclusion = false, defaultParam;
147
- for (let j = 2; j < length; j++) {
146
+ let defaultVal = '', j = 2,
147
+ /**
148
+ * - `1` 表示默认值
149
+ * - `2` 表示匹配值
150
+ */
151
+ found = 0, transclusion = false, defaultParam;
152
+ for (; j < length; j++) {
148
153
  const { anon, value, lastChild, name: option } = c[j];
149
154
  transclusion = /\0\d+t\x7F/u.test(anon ? value : option);
150
155
  if (anon) {
151
- if (j === length - 1) {
152
- // @ts-expect-error sparse array
153
- accum[accum.indexOf(lastChild)] = undefined;
154
- return implicitNewLine(value, prev);
156
+ if (j === length - 1) { // 位于最后的匿名参数是默认值
157
+ defaultParam = lastChild;
158
+ defaultVal = value;
155
159
  }
156
- else if (transclusion) {
160
+ else if (transclusion) { // 不支持复杂参数
157
161
  break;
158
162
  }
159
- else {
160
- found ||= cmp(var1, (0, string_1.decodeHtml)(value)) || value === '#default';
163
+ else if (cmp(var1, (0, string_1.decodeHtml)(value))) { // 下一个命名参数视为匹配值
164
+ found = 2;
165
+ }
166
+ else if (value === '#default' && found !== 2) { // 下一个命名参数视为默认值
167
+ found = 1;
161
168
  }
162
169
  }
163
- else if (transclusion) {
170
+ else if (transclusion) { // 不支持复杂参数
164
171
  break;
165
172
  }
166
- else if (found || cmp(var1, (0, string_1.decodeHtml)(option))) {
173
+ else if (found === 2 || cmp(var1, (0, string_1.decodeHtml)(option))) { // 第一个匹配值
167
174
  // @ts-expect-error sparse array
168
175
  accum[accum.indexOf(lastChild)] = undefined;
169
176
  return implicitNewLine(value, prev);
170
177
  }
171
- else if (option.toLowerCase() === '#default') {
172
- defaultVal = value;
178
+ else if (found === 1 || option.toLowerCase() === '#default') { // 更新默认值
173
179
  defaultParam = lastChild;
180
+ defaultVal = value;
181
+ found = 0;
174
182
  }
175
- if (j === length - 1) {
176
- if (defaultParam) {
177
- // @ts-expect-error sparse array
178
- accum[accum.indexOf(defaultParam)] = undefined;
179
- }
180
- return implicitNewLine(defaultVal, prev);
183
+ }
184
+ if (j === length) { // 不含复杂参数
185
+ if (defaultParam) {
186
+ // @ts-expect-error sparse array
187
+ accum[accum.indexOf(defaultParam)] = undefined;
181
188
  }
189
+ return implicitNewLine(defaultVal, prev);
182
190
  }
183
191
  }
184
192
  return m;
@@ -110,7 +110,7 @@ transclude_1.TranscludeToken.prototype.fixDuplication =
110
110
  values.set(val, [arg]);
111
111
  }
112
112
  }
113
- let noMoreAnon = anonCount === 0 || !key.trim() || isNaN(key);
113
+ let noMoreAnon = anonCount === 0 || !key.trim() || !Number.isInteger(Number(key));
114
114
  const emptyArgs = values.get('') ?? [], duplicatedArgs = [...values].filter(([val, { length }]) => val && length > 1).flatMap(([, curArgs]) => {
115
115
  const anonIndex = noMoreAnon ? -1 : curArgs.findIndex(({ anon }) => anon);
116
116
  if (anonIndex !== -1) {
@@ -157,7 +157,7 @@ transclude_1.TranscludeToken.prototype.fixDuplication =
157
157
  if (remaining > 1) {
158
158
  index_1.default.error(`${this.type === 'template'
159
159
  ? this.name
160
- : this.normalizeTitle(this.childNodes[1].toString(true), 828, { temporary: true })
160
+ : this.normalizeTitle(this.childNodes[1].text(), 828, { temporary: true })
161
161
  .title} still has ${remaining} duplicated ${key} parameters:\n${[...this.getArgs(key)].map(arg => {
162
162
  const { top, left } = arg.getBoundingClientRect();
163
163
  return `Line ${String(top)} Column ${String(left)}`;
package/dist/base.d.mts CHANGED
@@ -17,6 +17,7 @@ export interface Config {
17
17
  readonly conversionTable?: [string, string][];
18
18
  readonly redirects?: [string, string][];
19
19
  }
20
+ export type ConfigData = Omit<Config, 'excludes'>;
20
21
  export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | '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
22
  export declare const stages: {
22
23
  redirect: number;
@@ -278,7 +279,7 @@ export interface LanguageService {
278
279
  setTargetWikipedia(wiki: string): Promise<void>;
279
280
  }
280
281
  export interface Parser {
281
- config: Config | string;
282
+ config: ConfigData | string;
282
283
  i18n: Record<string, string> | string | undefined;
283
284
  /** @since v1.9.0 */
284
285
  viewOnly: boolean;
@@ -288,7 +289,7 @@ export interface Parser {
288
289
  * 获取当前的解析设置
289
290
  * @param config unprocessed parser configuration / 未处理的解析设置
290
291
  */
291
- getConfig(config?: Config): Config;
292
+ getConfig(config?: ConfigData): Config;
292
293
  /**
293
294
  * Parse wikitext
294
295
  *
package/dist/base.d.ts CHANGED
@@ -17,6 +17,7 @@ export interface Config {
17
17
  readonly conversionTable?: [string, string][];
18
18
  readonly redirects?: [string, string][];
19
19
  }
20
+ export type ConfigData = Omit<Config, 'excludes'>;
20
21
  export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | '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
22
  export declare const stages: {
22
23
  redirect: number;
@@ -278,7 +279,7 @@ export interface LanguageService {
278
279
  setTargetWikipedia(wiki: string): Promise<void>;
279
280
  }
280
281
  export interface Parser {
281
- config: Config | string;
282
+ config: ConfigData | string;
282
283
  i18n: Record<string, string> | string | undefined;
283
284
  /** @since v1.9.0 */
284
285
  viewOnly: boolean;
@@ -288,7 +289,7 @@ export interface Parser {
288
289
  * 获取当前的解析设置
289
290
  * @param config unprocessed parser configuration / 未处理的解析设置
290
291
  */
291
- getConfig(config?: Config): Config;
292
+ getConfig(config?: ConfigData): Config;
292
293
  /**
293
294
  * Parse wikitext
294
295
  *
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Config, LintError, TokenTypes, Parser as ParserBase, Stage, AST } from './base';
1
+ import type { Config, ConfigData, LintError, TokenTypes, Parser as ParserBase, Stage, AST } from './base';
2
2
  import type { Title, TitleOptions } from './lib/title';
3
3
  import type { LanguageService, QuickFixData } from './lib/lsp';
4
4
  import type { Token } from './internal';
@@ -31,6 +31,15 @@ declare interface Parser extends ParserBase {
31
31
  * @since v1.16.1
32
32
  */
33
33
  createLanguageService(uri: object): LanguageService;
34
+ /**
35
+ * Get the parser configuration for a MediaWiki project with Extension:CodeMirror installed
36
+ *
37
+ * 获取一个安装了CodeMirror扩展的MediaWiki项目的解析设置
38
+ * @param site site nickname / 网站别名
39
+ * @param url script path / 脚本路径
40
+ * @since v1.18.4
41
+ */
42
+ fetchConfig(site: string, url: string): Promise<Config>;
34
43
  /**
35
44
  * Check if the title is an interwiki link
36
45
  *
@@ -43,6 +52,6 @@ declare const Parser: Parser;
43
52
  // @ts-expect-error mixed export styles
44
53
  export = Parser;
45
54
  export default Parser;
46
- export type { Config, LintError, TokenTypes, LanguageService, QuickFixData, AST, };
55
+ export type { Config, ConfigData, LintError, TokenTypes, LanguageService, QuickFixData, AST, };
47
56
  export type * from './internal';
48
57
  declare global { type Acceptable = unknown; }
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ const redirectMap_1 = require("./lib/redirectMap");
15
15
  const fs_1 = __importDefault(require("fs"));
16
16
  const path_1 = __importDefault(require("path"));
17
17
  const diff_1 = require("./util/diff");
18
+ const config_1 = __importDefault(require("./bin/config"));
18
19
  /* NOT FOR BROWSER ONLY */
19
20
  /**
20
21
  * 从根路径require
@@ -28,7 +29,7 @@ const rootRequire = (file, dir) => require(path_1.default.isAbsolute(file)
28
29
  /* NOT FOR BROWSER */
29
30
  const promises = [Promise.resolve()];
30
31
  /^(zh|en)\s*:/diu; // eslint-disable-line @typescript-eslint/no-unused-expressions
31
- const getInterwikiRegex = (0, common_1.getObjRegex)(interwiki => new RegExp(String.raw `^(${interwiki.join('|')})\s*:`, 'diu'));
32
+ const getInterwikiRegex = (0, common_1.getRegex)(interwiki => new RegExp(String.raw `^(${interwiki.join('|')})\s*:`, 'diu'));
32
33
  let viewOnly = false, redirectMap = new redirectMap_1.RedirectMap();
33
34
  /* NOT FOR BROWSER END */
34
35
  const Parser = {
@@ -245,6 +246,12 @@ const Parser = {
245
246
  return tasks.get(uri) ?? new LanguageService(uri);
246
247
  }
247
248
  },
249
+ /* NOT FOR BROWSER ONLY */
250
+ /** @implements */
251
+ async fetchConfig(site, url) {
252
+ return this.getConfig(await (0, config_1.default)(site, url, false, true));
253
+ },
254
+ /* NOT FOR BROWSER ONLY */
248
255
  /* NOT FOR BROWSER */
249
256
  /** @implements */
250
257
  warn(msg, ...args) {
@@ -3,6 +3,10 @@ import type { TextDocument } from 'vscode-languageserver-textdocument';
3
3
  import type { JSONDocument } from 'vscode-json-languageservice';
4
4
  import type { Stylesheet } from 'vscode-css-languageservice';
5
5
  import type { Token } from '../internal';
6
+ declare interface Jax {
7
+ tex2mml(tex: string): string;
8
+ }
9
+ export declare const MathJax: Promise<Jax | undefined>;
6
10
  export declare const jsonTags: string[];
7
11
  export declare const jsonLSP: import("vscode-json-languageservice").LanguageService | undefined;
8
12
  export declare const cssLSP: import("vscode-css-languageservice").LanguageService | undefined;
@@ -3,12 +3,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.EmbeddedCSSDocument = exports.EmbeddedJSONDocument = exports.stylelint = exports.cssLSP = exports.jsonLSP = exports.jsonTags = void 0;
6
+ exports.EmbeddedCSSDocument = exports.EmbeddedJSONDocument = exports.stylelint = exports.cssLSP = exports.jsonLSP = exports.jsonTags = exports.MathJax = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const common_1 = require("@bhsd/common");
9
9
  /* NOT FOR BROWSER */
10
10
  const constants_1 = require("../util/constants");
11
- /* NOT FOR BROWSER END */
11
+ exports.MathJax = (async () => {
12
+ try {
13
+ const jax = require('mathjax');
14
+ return await jax.init({
15
+ loader: {
16
+ load: ['input/tex', '[tex]/mhchem'],
17
+ },
18
+ tex: {
19
+ packages: { '[+]': ['mhchem'] },
20
+ /** @ignore */
21
+ formatError(_, error) {
22
+ throw error;
23
+ },
24
+ },
25
+ startup: { typeset: false },
26
+ });
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ })();
12
32
  exports.jsonTags = ['templatedata', 'mapframe', 'maplink'];
13
33
  exports.jsonLSP = (() => {
14
34
  try {
package/dist/lib/lsp.js CHANGED
@@ -19,13 +19,12 @@ const util_1 = __importDefault(require("util"));
19
19
  const child_process_1 = require("child_process");
20
20
  const crypto_1 = require("crypto");
21
21
  const stylelint_1 = require("@bhsd/common/dist/stylelint");
22
- const config_1 = __importDefault(require("../bin/config"));
23
22
  const document_1 = require("./document");
24
23
  /** @see https://www.npmjs.com/package/stylelint-config-recommended */
25
24
  const cssRules = {
26
25
  'block-no-empty': null,
27
26
  'property-no-unknown': null,
28
- }, jsonSelector = document_1.jsonTags.map(s => `ext-inner#${s}`).join(), scores = new Map();
27
+ }, jsonSelector = document_1.jsonTags.map(s => `ext#${s}`).join(), mathSelector = ['math', 'chem', 'ce'].map(s => `ext#${s}`).join(), scores = new Map();
29
28
  let colors;
30
29
  /* NOT FOR BROWSER ONLY END */
31
30
  exports.tasks = new WeakMap();
@@ -775,8 +774,11 @@ class LanguageService {
775
774
  });
776
775
  })() :
777
776
  [], jsonDiagnostics = document_1.jsonLSP ?
778
- await Promise.all(root.querySelectorAll(jsonSelector).map(async (token) => {
779
- const textDoc = new document_1.EmbeddedJSONDocument(root, token), severityLevel = token.name === 'templatedata' ? 'error' : 'ignore', e = (await document_1.jsonLSP.doValidation(textDoc, textDoc.jsonDoc, {
777
+ await Promise.all(root.querySelectorAll(jsonSelector).map(async ({ name, lastChild, selfClosing }) => {
778
+ if (selfClosing) {
779
+ return [];
780
+ }
781
+ const textDoc = new document_1.EmbeddedJSONDocument(root, lastChild), severityLevel = name === 'templatedata' ? 'error' : 'ignore', e = (await document_1.jsonLSP.doValidation(textDoc, textDoc.jsonDoc, {
780
782
  comments: severityLevel,
781
783
  trailingCommas: severityLevel,
782
784
  })).map((error) => ({
@@ -831,6 +833,32 @@ class LanguageService {
831
833
  }));
832
834
  }
833
835
  }
836
+ const MathJax = await document_1.MathJax, mathDiagnostics = MathJax
837
+ ? root.querySelectorAll(mathSelector)
838
+ .map(({ selfClosing, innerText, lastChild, name }) => {
839
+ if (selfClosing) {
840
+ return [];
841
+ }
842
+ try {
843
+ MathJax.tex2mml(name === 'math' ? innerText : String.raw `\ce{${innerText}}`);
844
+ return [];
845
+ }
846
+ catch (e) {
847
+ if (e && typeof e === 'object' && 'id' in e && 'message' in e) {
848
+ return [
849
+ {
850
+ range: createNodeRange(lastChild),
851
+ severity: 1,
852
+ source: 'MathJax',
853
+ code: e.id,
854
+ message: e.message,
855
+ },
856
+ ];
857
+ }
858
+ return [];
859
+ }
860
+ })
861
+ : [];
834
862
  /* NOT FOR BROWSER ONLY END */
835
863
  return [
836
864
  diagnostics,
@@ -838,6 +866,7 @@ class LanguageService {
838
866
  jsonDiagnostics,
839
867
  /* NOT FOR BROWSER ONLY */
840
868
  lilypondDiagnostics,
869
+ mathDiagnostics,
841
870
  ].flat(2);
842
871
  }
843
872
  /**
@@ -890,8 +919,10 @@ class LanguageService {
890
919
  }
891
920
  /* NOT FOR BROWSER ONLY */
892
921
  if (document_1.jsonLSP) {
893
- for (const token of root.querySelectorAll(jsonSelector)) {
894
- ranges.push(...document_1.jsonLSP.getFoldingRanges(new document_1.EmbeddedJSONDocument(root, token)));
922
+ for (const { selfClosing, lastChild } of root.querySelectorAll(jsonSelector)) {
923
+ if (!selfClosing) {
924
+ ranges.push(...document_1.jsonLSP.getFoldingRanges(new document_1.EmbeddedJSONDocument(root, lastChild)));
925
+ }
895
926
  }
896
927
  }
897
928
  /* NOT FOR BROWSER ONLY END */
@@ -1122,13 +1153,13 @@ class LanguageService {
1122
1153
  }
1123
1154
  else if (type === 'magic-word-name') {
1124
1155
  info = this.#getParserFunction(parentNode.name);
1125
- f = offsetNode.toString(true).trim();
1156
+ f = offsetNode.text().trim();
1126
1157
  colon = parentNode.getAttribute('colon');
1127
1158
  }
1128
1159
  else if (offsetNode.is('magic-word') && !offsetNode.modifier && length === 1
1129
1160
  && (offset > 0 || root.posFromIndex(offsetNode.getAbsoluteIndex()).left === position.character)) {
1130
1161
  info = this.#getParserFunction(name);
1131
- f = offsetNode.firstChild.toString(true).trim();
1162
+ f = offsetNode.firstChild.text().trim();
1132
1163
  colon = offsetNode.getAttribute('colon');
1133
1164
  }
1134
1165
  else if ((offsetNode.is('magic-word') || offsetNode.is('template'))
@@ -1215,7 +1246,7 @@ class LanguageService {
1215
1246
  }
1216
1247
  const n = childNodes.length - 1, candidates = info.signatures.filter(params => (params.length >= n || params[params.length - 1]?.rest)
1217
1248
  && params.every(({ label, const: c }, i) => {
1218
- const p = c && i < n && childNodes[i + 1]?.toString(true).trim();
1249
+ const p = c && i < n && childNodes[i + 1]?.text().trim();
1219
1250
  return !p || label.toLowerCase().includes(p.toLowerCase());
1220
1251
  }));
1221
1252
  if (candidates.length === 0) {
@@ -1228,7 +1259,7 @@ class LanguageService {
1228
1259
  break;
1229
1260
  }
1230
1261
  }
1231
- const f = firstChild.toString(true).trim(), colon = lastChild.getAttribute('colon');
1262
+ const f = firstChild.text().trim(), colon = lastChild.getAttribute('colon');
1232
1263
  return {
1233
1264
  signatures: candidates.map((params) => ({
1234
1265
  label: `{{${f}${params.length === 0 ? '' : colon}${params.map(({ label }) => label).join('|')}}}`,
@@ -1343,7 +1374,7 @@ class LanguageService {
1343
1374
  this.config = index_1.default.getConfig(config);
1344
1375
  }
1345
1376
  catch {
1346
- this.config = index_1.default.getConfig(await (0, config_1.default)(site, `${mt[0]}/w`, false, true));
1377
+ this.config = await index_1.default.fetchConfig(site, `${mt[0]}/w`);
1347
1378
  }
1348
1379
  Object.assign(this.config, { articlePath: `${mt[0]}/wiki/` });
1349
1380
  }
package/dist/lib/text.js CHANGED
@@ -177,12 +177,13 @@ let AstText = (() => {
177
177
  constructor(text) {
178
178
  super();
179
179
  Object.defineProperties(this, {
180
- childNodes: { enumerable: false, configurable: false },
181
180
  data: {
182
181
  value: text,
183
182
  /* NOT FOR BROWSER */
184
183
  writable: false,
185
184
  },
185
+ /* NOT FOR BROWSER */
186
+ childNodes: { enumerable: false, configurable: false },
186
187
  });
187
188
  }
188
189
  /** @private */
@@ -237,12 +238,13 @@ let AstText = (() => {
237
238
  ]);
238
239
  for (let mt = errorRegex.exec(data); mt; mt = errorRegex.exec(data)) {
239
240
  const [, tag, prefix] = mt;
240
- let { index } = mt, error = mt[0].toLowerCase();
241
+ let { index, 0: error } = mt;
241
242
  if (prefix && prefix !== ']') {
242
243
  const { length } = prefix;
243
244
  index += length;
244
245
  error = error.slice(length);
245
246
  }
247
+ error = error.toLowerCase();
246
248
  const { 0: char, length } = error, magicLink = char === 'r' || char === 'p' || char === 'i';
247
249
  if (char === '<' && !tags.has(tag.toLowerCase())
248
250
  || char === '[' && type === 'ext-link-text' && (/&(?:rbrack|#93|#x5[Dd];);/u.test(data.slice(index + 1))
@@ -11,14 +11,33 @@ const constants_1 = require("../util/constants");
11
11
  /* NOT FOR BROWSER END */
12
12
  /* NOT FOR BROWSER ONLY */
13
13
  const v8_1 = require("v8");
14
- const MAXHEAP = (0, v8_1.getHeapStatistics)().total_available_size * 0.9, MAXITER = MAXHEAP / 2e5;
14
+ const MAXHEAP = (0, v8_1.getHeapStatistics)().heap_size_limit * 0.9;
15
15
  /* NOT FOR BROWSER ONLY END */
16
16
  const closes = {
17
17
  '=': String.raw `\n(?!(?:[^\S\n]|\0\d+[cn]\x7F)*\n)`,
18
18
  '{': String.raw `\}{2,}|\|`,
19
19
  '-': String.raw `\}-`,
20
20
  '[': String.raw `\]\]`,
21
- }, openBraces = String.raw `|\{{2,}`, marks = new Map([['!', '!'], ['!!', '+'], ['(!', '{'], ['!)', '}'], ['!-', '-'], ['=', '~'], ['server', 'm']]), getExecRegex = (0, common_1.getRegex)(s => new RegExp(s, 'gmu'));
21
+ }, lbrack = String.raw `\[(?!\[)`, newline = String.raw `\n(?![=\0])`, openBraces = String.raw `|\{{2,}`, marks = new Map([['!', '!'], ['!!', '+'], ['(!', '{'], ['!)', '}'], ['!-', '-'], ['=', '~'], ['server', 'm']]), getExecRegex = (0, common_1.getRegex)(s => new RegExp(s, 'gmu'));
22
+ let reReplace;
23
+ /* NOT FOR BROWSER ONLY */
24
+ try {
25
+ reReplace = new RegExp(String.raw `(?<!\{)\{\{((?:[^\n{}[]|${lbrack}|${newline})*)\}\}` // eslint-disable-line prefer-template
26
+ + '|'
27
+ + String.raw `\{\{((?:[^\n{}[]|${lbrack}|${newline})*)\}\}(?!\})`
28
+ + '|'
29
+ + String.raw `\[\[(?:[^\n[\]{]|${newline})*\]\]`
30
+ + '|'
31
+ + String.raw `-\{(?:[^\n{}[]|${lbrack}|${newline})*\}-`, 'gu');
32
+ }
33
+ catch {
34
+ /* NOT FOR BROWSER ONLY END */
35
+ reReplace = new RegExp(String.raw `\{\{((?:[^\n{}[]|${lbrack}|${newline})*)\}\}(?!\})` // eslint-disable-line prefer-template
36
+ + '|'
37
+ + String.raw `\[\[(?:[^\n[\]{]|${newline})*\]\]`
38
+ + '|'
39
+ + String.raw `-\{(?:[^\n{}[]|${lbrack}|${newline})*\}-`, 'gu');
40
+ }
22
41
  /**
23
42
  * 获取模板或魔术字对应的字符
24
43
  * @param s 模板或魔术字名
@@ -45,7 +64,7 @@ const getSymbol = (s) => {
45
64
  * @throws `TranscludeToken.constructor()`
46
65
  */
47
66
  const parseBraces = (wikitext, config, accum) => {
48
- const source = String.raw `${config.excludes?.includes('heading') ? '' : String.raw `^((?:\0\d+[cno]\x7F)*)={1,6}|`}\[\[|-\{(?!\{)`, { parserFunction: [, , , subst] } = config, stack = [], linkStack = [];
67
+ const source = String.raw `${config.excludes.includes('heading') ? '' : String.raw `^((?:\0\d+[cno]\x7F)*)={1,6}|`}\[\[|-\{(?!\{)`, { parserFunction: [, , , subst] } = config, stack = [], linkStack = [];
49
68
  /**
50
69
  * 恢复内链
51
70
  * @param s 不含内链的字符串
@@ -61,38 +80,45 @@ const parseBraces = (wikitext, config, accum) => {
61
80
  const push = (text, parts, lastIndex, index) => {
62
81
  parts[parts.length - 1].push(restore(text.slice(lastIndex, index)));
63
82
  };
64
- wikitext = wikitext.replace(/\{\{([^\n{}[]*)\}\}(?!\})|\[\[[^\n[\]{]*\]\]|-\{[^\n{}[]*\}-/gu, (m, p1) => {
65
- if (p1 !== undefined) {
66
- try {
67
- const { length } = accum, parts = p1.split('|');
68
- // @ts-expect-error abstract class
69
- new transclude_1.TranscludeToken(parts[0], parts.slice(1).map(part => {
70
- const i = part.indexOf('=');
71
- return i === -1 ? [part] : [part.slice(0, i), part.slice(i + 1)];
72
- }), config, accum);
73
- return `\0${length}${getSymbol(parts[0])}\x7F`;
74
- }
75
- catch (e) {
76
- /* istanbul ignore if */
77
- if (!(e instanceof SyntaxError) || e.message !== 'Invalid template name') {
78
- throw e;
83
+ let replaced;
84
+ do {
85
+ if (replaced !== undefined) {
86
+ wikitext = replaced;
87
+ }
88
+ replaced = wikitext.replace(reReplace, (m, p1, p2) => {
89
+ if (p1 !== undefined || typeof p2 === 'string') {
90
+ try {
91
+ const { length } = accum, parts = (p1 ?? p2).split('|');
92
+ // @ts-expect-error abstract class
93
+ new transclude_1.TranscludeToken(restore(parts[0]), parts.slice(1).map(part => {
94
+ const i = part.indexOf('=');
95
+ return (i === -1 ? [part] : [part.slice(0, i), part.slice(i + 1)]).map(restore);
96
+ }), config, accum);
97
+ return `\0${length}${getSymbol(parts[0])}\x7F`;
98
+ }
99
+ catch (e) {
100
+ /* istanbul ignore if */
101
+ if (!(e instanceof SyntaxError) || e.message !== 'Invalid template name') {
102
+ throw e;
103
+ }
79
104
  }
80
105
  }
81
- }
82
- linkStack.push(m);
83
- return `\0${linkStack.length - 1}\x7F`;
84
- });
106
+ linkStack.push(restore(m));
107
+ return `\0${linkStack.length - 1}\x7F`;
108
+ });
109
+ } while (replaced !== wikitext);
110
+ wikitext = replaced;
85
111
  const lastBraces = wikitext.lastIndexOf('}}') - wikitext.length;
86
112
  let moreBraces = lastBraces + wikitext.length !== -1;
87
113
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
88
114
  /^((?:\0\d+[cno]\x7F)*)={1,6}|\[\[|-\{(?!\{)|\{{2,}|\n(?!(?:[^\S\n]|\0\d+[cn]\x7F)*\n)|[|=]|\}{2,}|\}-|\]\]/gmu;
89
- let regex = getExecRegex(source + (moreBraces ? openBraces : '')), mt = regex.exec(wikitext), iter = 0, lastIndex;
115
+ let regex = getExecRegex(source + (moreBraces ? openBraces : '')), mt = regex.exec(wikitext), lastIndex;
90
116
  while (mt
91
117
  || lastIndex !== undefined && lastIndex <= wikitext.length
92
118
  && stack[stack.length - 1]?.[0]?.startsWith('=')) {
93
119
  /* NOT FOR BROWSER ONLY */
94
- if (iter++ > MAXITER && process.memoryUsage().heapUsed > MAXHEAP) {
95
- throw new RangeError('Maximum iteration exceeded');
120
+ if (process.memoryUsage().heapUsed > MAXHEAP) {
121
+ throw new RangeError('Maximum heap size exceeded');
96
122
  }
97
123
  /* NOT FOR BROWSER ONLY END */
98
124
  if (mt?.[1]) {
@@ -111,11 +137,13 @@ const parseBraces = (wikitext, config, accum) => {
111
137
  const rmt = /^(={1,6})(.+)\1((?:\s|\0\d+[cn]\x7F)*)$/u
112
138
  .exec(wikitext.slice(index, curIndex));
113
139
  if (rmt) {
114
- wikitext = `${wikitext.slice(0, index)}\0${accum.length}h\x7F${wikitext.slice(curIndex)}`;
115
- lastIndex = index + 4 + String(accum.length).length;
116
140
  rmt[2] = restore(rmt[2]);
117
- // @ts-expect-error abstract class
118
- new heading_1.HeadingToken(rmt[1].length, rmt.slice(2), config, accum);
141
+ if (!rmt[2].includes('\n')) {
142
+ wikitext = `${wikitext.slice(0, index)}\0${accum.length}h\x7F${wikitext.slice(curIndex)}`;
143
+ lastIndex = index + 4 + String(accum.length).length;
144
+ // @ts-expect-error abstract class
145
+ new heading_1.HeadingToken(rmt[1].length, rmt.slice(2), config, accum);
146
+ }
119
147
  }
120
148
  }
121
149
  }
@@ -14,7 +14,7 @@ const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', {
14
14
  const noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly';
15
15
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
16
16
  /<!--[\s\S]*?(?:-->|$)|<foo(?:\s[^>]*)?\/?>|<\/foo\s*>|<(bar)(\s[^>]*?)?(?:\/>|>([\s\S]*?)<\/(\1\s*)>)|<(baz)(\s[^>]*?)?(?:\/>|>([\s\S]*?)(?:<\/(baz\s*)>|$))/giu;
17
- return (0, common_1.getObjRegex)(ext => new RegExp(String.raw `<!--[\s\S]*?(?:-->|$)|<${noincludeRegex}(?:\s[^>]*)?/?>|</${noincludeRegex}\s*>|<(${ext.join('|')})(\s[^>]*?)?(?:/>|>([\s\S]*?)</(\1\s*)>)|<(${includeRegex})(\s[^>]*?)?(?:/>|>([\s\S]*?)(?:</(${includeRegex}\s*)>|$))`, 'giu'));
17
+ return (0, common_1.getRegex)(ext => new RegExp(String.raw `<!--[\s\S]*?(?:-->|$)|<${noincludeRegex}(?:\s[^>]*)?/?>|</${noincludeRegex}\s*>|<(${ext.join('|')})(\s[^>]*?)?(?:/>|>([\s\S]*?)</(\1\s*)>)|<(${includeRegex})(\s[^>]*?)?(?:/>|>([\s\S]*?)(?:</(${includeRegex}\s*)>|$))`, 'giu'));
18
18
  });
19
19
  /**
20
20
  * 更新`<onlyinclude>`和`</onlyinclude>`的位置
package/dist/src/arg.js CHANGED
@@ -130,7 +130,7 @@ let ArgToken = (() => {
130
130
  /** 设置name */
131
131
  #setName() {
132
132
  // eslint-disable-next-line no-unused-labels
133
- LSP: this.setAttribute('name', this.firstChild.toString(true).trim());
133
+ LSP: this.setAttribute('name', this.firstChild.text().trim());
134
134
  }
135
135
  /** @private */
136
136
  afterBuild() {
@@ -163,7 +163,7 @@ let AttributeToken = (() => {
163
163
  if (this.parentNode) {
164
164
  this.#tag = this.parentNode.name;
165
165
  }
166
- this.setAttribute('name', this.firstChild.toString(true).trim().toLowerCase());
166
+ this.setAttribute('name', this.firstChild.text().trim().toLowerCase());
167
167
  super.afterBuild();
168
168
  }
169
169
  /** @private */
@@ -25,7 +25,7 @@ const getUrlRegex = (0, common_1.getRegex)(protocol => new RegExp(String.raw `^(
25
25
  const getSyntaxRegex = (0, common_1.getRegex)(syntax => new RegExp(String.raw `^(\s*(?!\s))${syntax.replace('$1', '(.*)')}${syntax.endsWith('$1') ? '(?=$|\n)' : ''}(\s*)$`, 'u'));
26
26
  exports.galleryParams = new Set(['alt', 'link', 'lang', 'page', 'caption']);
27
27
  function validate(key, val, config, halfParsed, ext) {
28
- val = val.trim();
28
+ val = (0, string_1.removeComment)(val).trim();
29
29
  let value = val.replace(key === 'link' ? /\0\d+[tq]\x7F/gu : /\0\d+t\x7F/gu, '').trim();
30
30
  switch (key) {
31
31
  case 'width':
@@ -152,10 +152,12 @@ class ImageParameterToken extends index_2.Token {
152
152
  this.setAttribute('name', param[1]);
153
153
  return;
154
154
  }
155
- super(str, {
156
- ...config,
157
- excludes: [...config.excludes ?? [], 'list'],
158
- }, accum);
155
+ super(str, config.excludes.includes('list')
156
+ ? config
157
+ : {
158
+ ...config,
159
+ excludes: [...config.excludes, 'list'],
160
+ }, accum);
159
161
  this.setAttribute('name', 'caption');
160
162
  this.setAttribute('stage', 7);
161
163
  /* NOT FOR BROWSER */