wikiparser-node 1.37.1 → 1.39.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 (49) hide show
  1. package/README.md +8 -8
  2. package/bundle/bundle-es8.min.js +23 -23
  3. package/bundle/bundle-lsp.min.js +26 -26
  4. package/bundle/bundle.min.js +21 -21
  5. package/dist/addon/attribute.js +2 -2
  6. package/dist/addon/link.js +3 -4
  7. package/dist/addon/table.js +2 -2
  8. package/dist/base.d.mts +4 -3
  9. package/dist/base.d.ts +4 -3
  10. package/dist/base.js +1 -0
  11. package/dist/base.mjs +1 -0
  12. package/dist/bin/config.js +5 -4
  13. package/{extensions → dist/extensions}/typings.d.ts +2 -1
  14. package/dist/index.js +8 -7
  15. package/dist/lib/lintConfig.js +6 -3
  16. package/dist/lib/lsp.d.ts +3 -1
  17. package/dist/lib/lsp.js +24 -17
  18. package/dist/lib/ranges.js +3 -0
  19. package/dist/lib/title.d.ts +9 -2
  20. package/dist/lib/title.js +32 -14
  21. package/dist/mixin/elementLike.d.ts +2 -2
  22. package/dist/parser/hrAndDoubleUnderscore.js +1 -1
  23. package/dist/parser/table.js +1 -1
  24. package/dist/render/magicWords.js +14 -10
  25. package/dist/render/syntaxhighlight.js +12 -8
  26. package/dist/src/attribute.js +1 -1
  27. package/dist/src/index.js +3 -3
  28. package/dist/src/link/base.d.ts +3 -2
  29. package/dist/src/link/base.js +3 -4
  30. package/dist/src/link/file.js +12 -0
  31. package/dist/src/link/redirectTarget.js +1 -1
  32. package/dist/src/nowiki/doubleUnderscore.js +1 -1
  33. package/dist/src/nowiki/index.js +1 -0
  34. package/dist/src/nowiki/listBase.js +1 -1
  35. package/dist/src/table/base.d.ts +5 -0
  36. package/dist/src/table/base.js +3 -10
  37. package/dist/src/table/index.d.ts +2 -2
  38. package/dist/src/table/td.js +8 -5
  39. package/dist/src/tagPair/translate.js +2 -2
  40. package/dist/src/transclude.js +4 -0
  41. package/dist/util/sharable.js +1 -1
  42. package/dist/util/sharable.mjs +2 -2
  43. package/dist/util/string.js +3 -2
  44. package/extensions/dist/base.js +1 -1
  45. package/extensions/dist/lsp.js +3 -2
  46. package/i18n/en.json +1 -0
  47. package/i18n/zh-hans.json +1 -0
  48. package/i18n/zh-hant.json +1 -0
  49. package/package.json +25 -21
package/dist/base.d.mts CHANGED
@@ -7,7 +7,7 @@ export interface Config {
7
7
  readonly variable: string[];
8
8
  readonly functionHook: string[];
9
9
  readonly parserFunction: [Record<string, string>, Record<string, string>, string[], string[]];
10
- readonly doubleUnderscore: [string[], string[], Record<string, string>?, Record<string, string>?];
10
+ readonly doubleUnderscore: [string[], string[], Record<string, string>, Record<string, string>];
11
11
  readonly protocol: string;
12
12
  readonly interwiki: string[];
13
13
  readonly img: Record<string, string>;
@@ -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 ["arg-in-ext", "bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "invalid-json", "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"];
50
+ export declare const rules: readonly ["arg-in-ext", "blank-alt", "bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "invalid-json", "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];
@@ -155,7 +155,7 @@ export interface LanguageService {
155
155
  *
156
156
  * 销毁实例
157
157
  */
158
- destroy(): void;
158
+ destroy(): Promise<void>;
159
159
  /**
160
160
  * Provide color decorators
161
161
  *
@@ -295,6 +295,7 @@ export interface LanguageService {
295
295
  * @param user URI for wiki userpage or email address of the user / 维基用户页面地址或用户的电子邮件地址
296
296
  */
297
297
  setTargetWikipedia(wiki: string, user: string): Promise<void>;
298
+ [Symbol.dispose](): void;
298
299
  }
299
300
  export type SeverityLevel = 0 | 1 | 2 | false | 'off' | 'warning' | 'error';
300
301
  export type LintConfigValue = SeverityLevel | [SeverityLevel, Record<string, SeverityLevel>?];
package/dist/base.d.ts CHANGED
@@ -7,7 +7,7 @@ export interface Config {
7
7
  readonly variable: string[];
8
8
  readonly functionHook: string[];
9
9
  readonly parserFunction: [Record<string, string>, Record<string, string>, string[], string[]];
10
- readonly doubleUnderscore: [string[], string[], Record<string, string>?, Record<string, string>?];
10
+ readonly doubleUnderscore: [string[], string[], Record<string, string>, Record<string, string>];
11
11
  readonly protocol: string;
12
12
  readonly interwiki: string[];
13
13
  readonly img: Record<string, string>;
@@ -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 ["arg-in-ext", "bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "invalid-json", "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"];
50
+ export declare const rules: readonly ["arg-in-ext", "blank-alt", "bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "invalid-json", "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];
@@ -155,7 +155,7 @@ export interface LanguageService {
155
155
  *
156
156
  * 销毁实例
157
157
  */
158
- destroy(): void;
158
+ destroy(): Promise<void>;
159
159
  /**
160
160
  * Provide color decorators
161
161
  *
@@ -295,6 +295,7 @@ export interface LanguageService {
295
295
  * @param user URI for wiki userpage or email address of the user / 维基用户页面地址或用户的电子邮件地址
296
296
  */
297
297
  setTargetWikipedia(wiki: string, user: string): Promise<void>;
298
+ [Symbol.dispose](): void;
298
299
  }
299
300
  export type SeverityLevel = 0 | 1 | 2 | false | 'off' | 'warning' | 'error';
300
301
  export type LintConfigValue = SeverityLevel | [SeverityLevel, Record<string, SeverityLevel>?];
package/dist/base.js CHANGED
@@ -36,6 +36,7 @@ exports.stages = (() => {
36
36
  exports.rules = (() => {
37
37
  const arr = [
38
38
  'arg-in-ext',
39
+ 'blank-alt',
39
40
  'bold-header',
40
41
  'format-leakage',
41
42
  'fostered-content',
package/dist/base.mjs CHANGED
@@ -33,6 +33,7 @@ const stages = /* @__PURE__ */ (() => {
33
33
  const rules = /* @__PURE__ */ (() => {
34
34
  const arr = [
35
35
  "arg-in-ext",
36
+ "blank-alt",
36
37
  "bold-header",
37
38
  "format-leakage",
38
39
  "fostered-content",
@@ -16,7 +16,8 @@ const diff_1 = require("../util/diff");
16
16
  * @param config.articlePath article path
17
17
  */
18
18
  const arrToObj = ({ articlePath, ...obj }) => {
19
- for (const [k, v] of Object.entries(obj)) {
19
+ for (const k in obj) {
20
+ const v = obj[k];
20
21
  if (Array.isArray(v) && v.every(x => typeof x === 'string')) {
21
22
  Object.assign(obj, { [k]: Object.fromEntries(v.map(x => [x, true])) });
22
23
  }
@@ -39,7 +40,7 @@ const filterGadget = (id) => {
39
40
  const n = Number(id);
40
41
  return n < 2300 || n > 2303; // Gadget, Gadget talk, Gadget definition, Gadget definition talk
41
42
  };
42
- const pkg = "wikiparser-node", version = "1.37.1";
43
+ const pkg = "wikiparser-node", version = "1.39.0";
43
44
  /**
44
45
  * Get the parser configuration for a Wikimedia Foundation project.
45
46
  * @param site site nickname
@@ -147,9 +148,9 @@ exports.default = async (site, url, user, force, internal) => {
147
148
  }
148
149
  else {
149
150
  const oldConfig = arrToObj(require(file)), newConfig = arrToObj(config);
150
- for (const [k, v] of Object.entries(newConfig)) {
151
+ for (const k in newConfig) {
151
152
  try {
152
- strict_1.default.deepStrictEqual(oldConfig[k], v);
153
+ strict_1.default.deepStrictEqual(oldConfig[k], newConfig[k]);
153
154
  }
154
155
  catch (e) {
155
156
  if (e instanceof strict_1.default.AssertionError) {
@@ -9,7 +9,7 @@ import type {
9
9
  CodeAction,
10
10
  } from 'vscode-languageserver-types';
11
11
  // 必须写在一行内
12
- import type {Config, ConfigData, LintConfig, LintError, AST, LanguageService} from '../base';
12
+ import type {Config, ConfigData, LintConfig, LintError, AST, LanguageService, SignatureData} from '../base';
13
13
 
14
14
  declare interface Test {
15
15
  desc: string;
@@ -40,6 +40,7 @@ export interface LanguageServiceBase extends Omit<
40
40
  LanguageService,
41
41
  'provideDocumentSymbols' | 'provideCodeAction'
42
42
  > {
43
+ data?: SignatureData;
43
44
  provideDocumentColors(text: string): Promise<ColorInformation[]>;
44
45
  provideColorPresentations(color: ColorInformation): Promise<ColorPresentation[]>;
45
46
  resolveCodeAction(rule?: string): Promise<CodeAction>;
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ const jsonRequire = (file) => {
30
30
  if (fullPath.endsWith('.json')) {
31
31
  return require(fullPath);
32
32
  }
33
+ /* c8 ignore next */
33
34
  throw new RangeError('Only JSON files are supported!');
34
35
  };
35
36
  /**
@@ -138,7 +139,7 @@ const Parser = {
138
139
  this.config = rootRequire(this.config, 'config');
139
140
  }
140
141
  /* c8 ignore next 3 */
141
- if (this.config.doubleUnderscore.length < 3 || !('functionHook' in this.config)) {
142
+ if (!('functionHook' in this.config)) {
142
143
  (0, diff_1.error)(`The schema (${path_1.default.join(__dirname, '..', 'config', '.schema.json')}) of parser configuration is updated.`);
143
144
  }
144
145
  return this.getConfig();
@@ -148,7 +149,7 @@ const Parser = {
148
149
  /* NOT FOR BROWSER */
149
150
  conversionTable, redirects, } = parserConfig;
150
151
  for (let i = 0; i < 2; i++) {
151
- if (doubleUnderscore.length > i + 2 && doubleUnderscore[i].length === 0) {
152
+ if (doubleUnderscore[i].length === 0) {
152
153
  doubleUnderscore[i] = Object.keys(doubleUnderscore[i + 2]);
153
154
  }
154
155
  }
@@ -245,7 +246,7 @@ const Parser = {
245
246
  catch (e) /* c8 ignore start */ {
246
247
  if (e instanceof Error) {
247
248
  const file = path_1.default.join(__dirname, '..', 'errors', new Date().toISOString()), stage = token.getAttribute('stage');
248
- for (const k of Object.keys(config)) {
249
+ for (const k in config) {
249
250
  if (k.startsWith('regex') || config[k] instanceof Set) {
250
251
  delete config[k];
251
252
  }
@@ -260,7 +261,7 @@ const Parser = {
260
261
  /* NOT FOR BROWSER ONLY END */
261
262
  });
262
263
  /* NOT FOR BROWSER */
263
- if (types?.includes('list-range')) {
264
+ if (maxStage > constants_1.MAX_STAGE || types?.includes('list-range')) {
264
265
  root.buildLists();
265
266
  }
266
267
  /* c8 ignore start */
@@ -356,8 +357,8 @@ const Parser = {
356
357
  args.push(arg[i]);
357
358
  delete arg[i];
358
359
  }
359
- for (const [key, value] of Object.entries(arg)) {
360
- args.push(`${key}=${value}`);
360
+ for (const key in arg) {
361
+ args.push(`${key}=${arg[key]}`);
361
362
  }
362
363
  }
363
364
  const { parserFunction } = this.getConfig(), [lcName, canonicalName] = (0, debug_1.getCanonicalName)(name, parserFunction), custom = constants_1.functionHooks.has(lcName);
@@ -490,7 +491,7 @@ const def = {
490
491
  'debugging',
491
492
  'isInterwiki',
492
493
  ]);
493
- for (const key of Object.keys(Parser)) {
494
+ for (const key in Parser) {
494
495
  if (!enumerable.has(key)) {
495
496
  def[key] = { enumerable: false };
496
497
  }
@@ -16,6 +16,7 @@ const dict = new Map([
16
16
  ]);
17
17
  const defaultLintRuleConfig = {
18
18
  'arg-in-ext': 1,
19
+ 'blank-alt': 1,
19
20
  'bold-header': [
20
21
  1,
21
22
  {
@@ -289,8 +290,8 @@ class LintRuleConfiguration {
289
290
  if (!config) {
290
291
  return;
291
292
  }
292
- for (const [key, value] of Object.entries(config)) {
293
- set(this, key, value);
293
+ for (const key in config) {
294
+ set(this, key, config[key]);
294
295
  }
295
296
  }
296
297
  /** @implements */
@@ -304,6 +305,7 @@ class LintRuleConfiguration {
304
305
  }
305
306
  /** 语法检查设置 */
306
307
  class LintConfiguration {
308
+ // @ts-expect-error lazy initialization
307
309
  #rules;
308
310
  /** @implements */
309
311
  get rules() {
@@ -331,7 +333,8 @@ class LintConfiguration {
331
333
  else {
332
334
  const { rules: ruleConfig, ...other } = (config ?? {});
333
335
  this.rules = ruleConfig;
334
- for (const [key, value] of Object.entries(other)) {
336
+ for (const key in other) {
337
+ const value = other[key];
335
338
  if (value !== undefined && Object.prototype.hasOwnProperty.call(defaultLintConfig, key)) {
336
339
  this[key] = value;
337
340
  }
package/dist/lib/lsp.d.ts CHANGED
@@ -16,7 +16,7 @@ export declare class LanguageService implements LanguageServiceBase {
16
16
  /** @param uri 任务标识 */
17
17
  constructor(uri: object);
18
18
  /** @implements */
19
- destroy(): void;
19
+ destroy(): Promise<void>;
20
20
  /**
21
21
  * Provide color decorators
22
22
  *
@@ -160,4 +160,6 @@ export declare class LanguageService implements LanguageServiceBase {
160
160
  * @since v1.18.1
161
161
  */
162
162
  setTargetWikipedia(wiki: string, user: string): Promise<void>;
163
+ /** @implements */
164
+ [Symbol.dispose](): void;
163
165
  }
package/dist/lib/lsp.js CHANGED
@@ -261,6 +261,7 @@ const partialParse = async (wikitext, watch, include, config = index_1.default.g
261
261
  set(parseOnce, 0);
262
262
  }
263
263
  else {
264
+ /* c8 ignore next */
264
265
  resolve();
265
266
  }
266
267
  },
@@ -354,13 +355,19 @@ const getSectionEnd = (section, lines, line) => {
354
355
  /* NOT FOR BROWSER ONLY END */
355
356
  /** VSCode-style language service */
356
357
  class LanguageService {
358
+ // @ts-expect-error lazy initialization
357
359
  #text;
360
+ // @ts-expect-error lazy initialization
358
361
  #text2;
359
362
  #running;
360
363
  #running2;
364
+ // @ts-expect-error lazy initialization
361
365
  #done;
366
+ // @ts-expect-error lazy initialization
362
367
  #done2;
368
+ // @ts-expect-error lazy initialization
363
369
  #config;
370
+ // @ts-expect-error lazy initialization
364
371
  #include;
365
372
  #completionConfig;
366
373
  /** @since v1.17.1 */
@@ -371,6 +378,7 @@ class LanguageService {
371
378
  data;
372
379
  /* NOT FOR BROWSER ONLY */
373
380
  /** @private */
381
+ // @ts-expect-error lazy initialization
374
382
  lilypond;
375
383
  #lilypondData;
376
384
  #mathData;
@@ -393,19 +401,12 @@ class LanguageService {
393
401
  });
394
402
  }
395
403
  /** @implements */
396
- destroy() {
404
+ async destroy() {
397
405
  Object.setPrototypeOf(this, null);
398
406
  /* NOT FOR BROWSER ONLY */
399
407
  const dir = path_1.default.join(__dirname, 'lilypond');
400
408
  if (fs_1.default.existsSync(dir)) {
401
- for (const file of fs_1.default.readdirSync(dir)) {
402
- (async () => {
403
- try {
404
- await fs_1.default.promises.unlink(path_1.default.join(dir, file));
405
- }
406
- catch { }
407
- })();
408
- }
409
+ await Promise.allSettled(fs_1.default.readdirSync(dir).map(file => fs_1.default.promises.unlink(path_1.default.join(dir, file))));
409
410
  }
410
411
  }
411
412
  /** 检查解析设置有无更新 */
@@ -505,6 +506,7 @@ class LanguageService {
505
506
  colors = new RegExp(String.raw `\b${Object.keys(colorName).join('|')}\b`, 'giu');
506
507
  }
507
508
  catch {
509
+ /* c8 ignore next */
508
510
  colors = false;
509
511
  }
510
512
  }
@@ -618,7 +620,7 @@ class LanguageService {
618
620
  * @param position position / 位置
619
621
  */
620
622
  async provideCompletionItems(text, position) {
621
- const { re, allTags, functions, switches, jaSwitches, protocols, params, tags, ext, } = this.#prepareCompletionConfig(), { line, character } = position, curLine = text.split(/\r?\n/u, line + 1)[line], mt = re.exec(curLine?.slice(0, character) ?? ''), [, , iAlias = {}, sAlias = {}] = this.config.doubleUnderscore;
623
+ const { re, allTags, functions, switches, jaSwitches, protocols, params, tags, ext, } = this.#prepareCompletionConfig(), { line, character } = position, curLine = text.split(/\r?\n/u, line + 1)[line], mt = re.exec(curLine?.slice(0, character) ?? ''), [, , iAlias, sAlias] = this.config.doubleUnderscore;
622
624
  if (mt?.[1] !== undefined) { // tag
623
625
  const closing = mt[1].startsWith('/');
624
626
  return getCompletion(allTags, 'Class', mt[1].slice(closing ? 1 : 0), position, closing && !curLine?.slice(character).trim().startsWith('>') ? '>' : '');
@@ -632,10 +634,10 @@ class LanguageService {
632
634
  else if (!isJa) {
633
635
  name = name.slice(2, -2);
634
636
  }
635
- if (name in iAlias) {
637
+ if (Object.hasOwn(iAlias, name)) {
636
638
  name = iAlias[name];
637
639
  }
638
- else if (name in sAlias) {
640
+ else if (Object.hasOwn(sAlias, name)) {
639
641
  name = sAlias[name];
640
642
  }
641
643
  return this.#getBehaviorSwitch(name.toLowerCase());
@@ -670,10 +672,10 @@ class LanguageService {
670
672
  if (!this.data) {
671
673
  return undefined;
672
674
  }
673
- else if (name in insensitive) {
675
+ else if (Object.hasOwn(insensitive, name)) {
674
676
  name = insensitive[name];
675
677
  }
676
- else if (name in sensitive) {
678
+ else if (Object.hasOwn(sensitive, name)) {
677
679
  name = sensitive[name];
678
680
  }
679
681
  return this.#getParserFunction(name.toLowerCase());
@@ -932,8 +934,9 @@ class LanguageService {
932
934
  let lilypondDiagnostics = [];
933
935
  if (this.lilypond) {
934
936
  const tokens = root.querySelectorAll('ext#score').filter(token => {
935
- const lang = token.getAttr('lang');
936
- return (lang === undefined || lang === 'lilypond') && token.innerText;
937
+ const lang = token.getAttr('lang'), { innerText } = token;
938
+ return (lang === undefined || lang === 'lilypond') && innerText?.trim()
939
+ && !/[#$](?!@?\s*(?:'\s*)?(?:[#"]|-?\.?\d|[a-z_][-:\w]*(?![^)\]}\s])))/iu.test(innerText);
937
940
  });
938
941
  if (tokens.length > 0) {
939
942
  const dir = path_1.default.join(__dirname, 'lilypond');
@@ -951,7 +954,7 @@ class LanguageService {
951
954
  fs_1.default.writeFileSync(file, score);
952
955
  try {
953
956
  // eslint-disable-next-line @typescript-eslint/strict-void-return
954
- await util_1.default.promisify(child_process_1.execFile)(this.lilypond, ['-s', '-o', dir, file]);
957
+ await util_1.default.promisify(child_process_1.execFile)(this.lilypond, ['-dno-print-pages', '-s', '-o', dir, file]);
955
958
  scores.set(score, []);
956
959
  }
957
960
  catch (e) {
@@ -1611,6 +1614,10 @@ class LanguageService {
1611
1614
  }
1612
1615
  Object.assign(this.config, { articlePath: `${host}/wiki/` });
1613
1616
  }
1617
+ /** @implements */
1618
+ [Symbol.dispose]() {
1619
+ void this.destroy();
1620
+ }
1614
1621
  }
1615
1622
  exports.LanguageService = LanguageService;
1616
1623
  constants_1.classes['LanguageService'] = __filename;
@@ -5,8 +5,11 @@ const constants_1 = require("../util/constants");
5
5
  const diff_1 = require("../util/diff");
6
6
  /** 模拟Python的Range对象。除`step`至少为`1`外,允许负数、小数或`end < start`的情形。 */
7
7
  class Range {
8
+ // @ts-expect-error lazy initialization
8
9
  start;
10
+ // @ts-expect-error lazy initialization
9
11
  end;
12
+ // @ts-expect-error lazy initialization
10
13
  step;
11
14
  /**
12
15
  * @param str 表达式
@@ -58,9 +58,9 @@ export declare class Title {
58
58
  */
59
59
  constructor(title: string, defaultNs: number, config: Config, { temporary, decode, selfLink, page }?: TitleOptions);
60
60
  /**
61
- * Check if the title is a redirect
61
+ * Check if the title is a redirect and get the redirect target
62
62
  *
63
- * 检测是否是重定向
63
+ * 检测是否是重定向并获取重定向目标
64
64
  * @since v1.12.2
65
65
  */
66
66
  getRedirection(): [boolean, string];
@@ -72,6 +72,13 @@ export declare class Title {
72
72
  * @since v1.10.0
73
73
  */
74
74
  getUrl(articlePath?: string): string;
75
+ /**
76
+ * Check if the title is a redirect
77
+ *
78
+ * 检测是否是重定向
79
+ * @since v1.38.2
80
+ */
81
+ isRedirect(): boolean;
75
82
  /**
76
83
  * Get the URL of the file
77
84
  *
package/dist/lib/title.js CHANGED
@@ -19,6 +19,7 @@ const resolve = (title) => {
19
19
  * MediaWiki页面标题对象
20
20
  */
21
21
  class Title {
22
+ // @ts-expect-error lazy initialization
22
23
  #main;
23
24
  #namespaces;
24
25
  #path;
@@ -200,8 +201,9 @@ class Title {
200
201
  /**
201
202
  * 生成标题
202
203
  * @param prefix 前缀
204
+ * @param redirect 是否允许重定向
203
205
  */
204
- #getTitle(prefix) {
206
+ #getTitle(prefix, redirect = true) {
205
207
  let title = (prefix + this.main).replace(/ /gu, '_');
206
208
  if (title.startsWith('/')) {
207
209
  title = (this.page ?? '') + title.replace(/(.)\/$/u, '$1');
@@ -213,21 +215,23 @@ class Title {
213
215
  }
214
216
  }
215
217
  /* NOT FOR BROWSER */
216
- const media = title.startsWith('Media:');
217
- let redirected = this.redirects.get(media ? `File:${title.slice(6)}` : title);
218
- if (redirected) {
219
- const hash = redirected.indexOf('#');
220
- this.#redirectFragment = hash === -1 ? undefined : redirected.slice(hash + 1);
221
- redirected = hash === -1 ? redirected : redirected.slice(0, hash);
222
- return [true, media ? redirected.replace(/^File:/u, 'Media:') : redirected];
218
+ if (redirect) {
219
+ const media = title.startsWith('Media:');
220
+ let redirected = this.redirects.get(media ? `File:${title.slice(6)}` : title);
221
+ if (redirected) {
222
+ const hash = redirected.indexOf('#');
223
+ this.#redirectFragment = hash === -1 ? undefined : redirected.slice(hash + 1);
224
+ redirected = hash === -1 ? redirected : redirected.slice(0, hash);
225
+ return [true, media ? redirected.replace(/^File:/u, 'Media:') : redirected];
226
+ }
223
227
  }
224
228
  /* NOT FOR BROWSER */
225
229
  return [false, title];
226
230
  }
227
231
  /**
228
- * Check if the title is a redirect
232
+ * Check if the title is a redirect and get the redirect target
229
233
  *
230
- * 检测是否是重定向
234
+ * 检测是否是重定向并获取重定向目标
231
235
  * @since v1.12.2
232
236
  */
233
237
  getRedirection() {
@@ -279,6 +283,15 @@ class Title {
279
283
  }
280
284
  }
281
285
  /* NOT FOR BROWSER */
286
+ /**
287
+ * Check if the title is a redirect
288
+ *
289
+ * 检测是否是重定向
290
+ * @since v1.38.2
291
+ */
292
+ isRedirect() {
293
+ return this.getRedirection()[0];
294
+ }
282
295
  /**
283
296
  * Get the URL of the file
284
297
  *
@@ -307,10 +320,15 @@ class Title {
307
320
  return expandMagicWord('filepath', [main, `${width || ''}${height ? `x${height}` : ''}`]);
308
321
  }
309
322
  /** @private */
310
- toString(display) {
311
- return (display ? this.displayTitle : this.title) + (this.#fragment === undefined && this.#redirectFragment === undefined
312
- ? ''
313
- : `#${this.#fragment ?? this.#redirectFragment}`);
323
+ toString(display, redirect = true) {
324
+ if (redirect) {
325
+ return (display ? this.displayTitle : this.title) + (this.#fragment === undefined && this.#redirectFragment === undefined
326
+ ? ''
327
+ : `#${this.#fragment ?? this.#redirectFragment}`);
328
+ }
329
+ const { prefix, interwiki } = this, [, result] = this.#getTitle(interwiki + (interwiki && ':') + prefix, false);
330
+ return (display ? result.replaceAll('_', ' ') : result)
331
+ + (this.#fragment === undefined ? '' : `#${this.#fragment}`);
314
332
  }
315
333
  /**
316
334
  * Perform unidirectional language conversion
@@ -1,4 +1,4 @@
1
- import type { AstNodes, Token } from '../internal';
1
+ import type { AstNodes, Token, HtmlToken, ExtToken } from '../internal';
2
2
  declare type ElementConstructor = abstract new (...args: any[]) => {
3
3
  readonly childNodes: readonly AstNodes[];
4
4
  detach?: () => void;
@@ -60,6 +60,6 @@ export interface ElementLike {
60
60
  * 标签名选择器
61
61
  * @param tag tag name / 标签名
62
62
  */
63
- getElementsByTagName<T = Token>(tag: string): T[];
63
+ getElementsByTagName<T extends HtmlToken | ExtToken = HtmlToken | ExtToken>(tag: string): T[];
64
64
  }
65
65
  /** @ignore */
@@ -34,7 +34,7 @@ const parseHrAndDoubleUnderscore = ({ firstChild: { data }, type, name }, config
34
34
  if (caseSensitive || caseInsensitive) {
35
35
  // @ts-expect-error abstract class
36
36
  new doubleUnderscore_1.DoubleUnderscoreToken(key, caseSensitive, Boolean(p4), config, accum);
37
- return `\0${accum.length - 1}${caseInsensitive && (aliases?.[lc] ?? /* c8 ignore next */ lc) === 'toc' ? 'u' : 'n'}\x7F`;
37
+ return `\0${accum.length - 1}${caseInsensitive && (aliases[lc] ?? /* c8 ignore next */ lc) === 'toc' ? 'u' : 'n'}\x7F`;
38
38
  }
39
39
  return m;
40
40
  });
@@ -92,7 +92,7 @@ const parseTable = ({ firstChild: { data }, type, name }, config, accum) => {
92
92
  else if (row) {
93
93
  top = pop(top, stack);
94
94
  if (top.is('tr')) {
95
- top = stack.pop();
95
+ top = stack.pop(); // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion
96
96
  }
97
97
  // @ts-expect-error abstract class
98
98
  const tr = new tr_1.TrToken(`\n${spaces}${row}`, attr, config, accum);
@@ -178,7 +178,11 @@ const parseUrl = ({ testServer = '', articlePath = testServer }) => {
178
178
  }
179
179
  let nsVal = Number(val);
180
180
  if (Number.isNaN(nsVal)) {
181
- nsVal = nsid[val.toLowerCase().replaceAll('_', ' ')];
181
+ const key = val.toLowerCase().replaceAll('_', ' ');
182
+ if (!Object.hasOwn(nsid, key)) {
183
+ return '';
184
+ }
185
+ nsVal = nsid[key];
182
186
  }
183
187
  return namespaces[nsVal] ?? '';
184
188
  }, dictUrl = {
@@ -277,14 +281,14 @@ const parseUrl = ({ testServer = '', articlePath = testServer }) => {
277
281
  }, pad = ([arg0, arg1, arg2 = '0'], method) => arg0.includes('\0') ? arg0 : arg0[method](Number(arg1), strip(arg2)), anchorencode = (0, string_1.replaceEntities)(), special = (target, config) => {
278
282
  const title = makeTitle(target, config, -1);
279
283
  return title && title.ns === -1 ? title.prefix + title.main : 'Special:Badtitle';
280
- }, contentmodels = {
281
- js: ['JavaScript', 'javascript'],
282
- css: ['CSS'],
283
- json: ['JSON'],
284
- vue: ['Vue'],
285
- 'sanitized-css': ['Sanitized CSS'],
286
- Scribunto: ['Scribunto module'],
287
- }, contentmodel = (i, extension) => contentmodels[extension][i] ?? extension, cmp = (x, y, decode) => {
284
+ }, contentmodels = new Map([
285
+ ['js', ['JavaScript', 'javascript']],
286
+ ['css', ['CSS']],
287
+ ['json', ['JSON']],
288
+ ['vue', ['Vue']],
289
+ ['sanitized-css', ['Sanitized CSS']],
290
+ ['Scribunto', ['Scribunto module']],
291
+ ]), contentmodel = (i, extension) => contentmodels.get(extension)[i] ?? extension, cmp = (x, y, decode) => {
288
292
  const a = decode ? (0, string_1.decodeHtml)(x) : x, b = (0, string_1.decodeHtml)(y);
289
293
  return a === b || Boolean(a && b) && Number(a) === Number(b);
290
294
  }, isError = (s) => /<(?:strong|span|p|div)\s+(?:[^\s>]+\s+)*?class="\s*(?:[^"\s>]+\s+)*?error(?:\s[^">]*)?"/u.test(s), splitArg = (arg) => {
@@ -577,7 +581,7 @@ const expandMagicWord = (name, args, page = '', config = index_1.default.getConf
577
581
  }
578
582
  return main.endsWith('/doc') ? 'wikiext' : contentmodel(i, 'Scribunto');
579
583
  }
580
- return (n === 8 || n === 2 && isSubpage) && extension && extension in contentmodels
584
+ return (n === 8 || n === 2 && isSubpage) && extension && contentmodels.has(extension)
581
585
  ? contentmodel(i, extension)
582
586
  : 'wikitext';
583
587
  }
@@ -389,7 +389,9 @@ const loadLanguage = (lang) => {
389
389
  if (lang in exports.Prism.languages) {
390
390
  return lang;
391
391
  }
392
- lang = aliases[lang] ?? lang;
392
+ else if (Object.hasOwn(aliases, lang)) {
393
+ lang = aliases[lang];
394
+ }
393
395
  if (lang === 'wiki') {
394
396
  try {
395
397
  const { default: registerWiki } = require('prism-wiki');
@@ -399,13 +401,15 @@ const loadLanguage = (lang) => {
399
401
  }
400
402
  catch { }
401
403
  }
402
- const dep = dependencies[lang];
403
- if (typeof dep === 'string') {
404
- (0, exports.loadLanguage)(dep);
405
- }
406
- else if (dep) {
407
- for (const d of dep) {
408
- (0, exports.loadLanguage)(d);
404
+ if (Object.hasOwn(dependencies, lang)) {
405
+ const dep = dependencies[lang];
406
+ if (typeof dep === 'string') {
407
+ (0, exports.loadLanguage)(dep);
408
+ }
409
+ else if (dep) {
410
+ for (const d of dep) {
411
+ (0, exports.loadLanguage)(d);
412
+ }
409
413
  }
410
414
  }
411
415
  require(`prismjs/components/prism-${lang}.js`);
@@ -224,7 +224,7 @@ let AttributeToken = (() => {
224
224
  // 不支持通用HTML属性的扩展标签
225
225
  type === 'ext-attr' && !attrs2
226
226
  // 或非通用HTML属性
227
- || !/^(?:xmlns:[\w:.-]+|data-(?!ooui|mw|parsoid)[^:]*)$/u.test(name)
227
+ || !/^(?:xmlns:[\w:.-]+|data-(?!ooui|mw|parsoid)[^:_]*)$/u.test(name)
228
228
  && (tag === 'meta' || tag === 'link' || !sharable_1.commonHtmlAttrs.has(name)))
229
229
  || (name === 'itemtype' || name === 'itemid' || name === 'itemref')
230
230
  && !parentNode?.hasAttr('itemscope')) {