wikilint 2.42.0 → 2.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,6 +16,25 @@ You can also directly lint Wikitext articles in the command line using this pack
16
16
  npx wikilint --config zhwiki --include --lang zh-hans *.wiki
17
17
  ```
18
18
 
19
+ ## Installation
20
+
21
+ ```sh
22
+ npm i wikilint
23
+ ```
24
+
25
+ ### Optional dependencies
26
+
27
+ You can install any of the following optional packages to enable advanced features:
28
+
29
+ | Package | Purpose |
30
+ | :-: | --- |
31
+ | `mathoid-texvcjs` | Linting `<math>` and `<chem>` extension tags |
32
+ | `vscode-css-languageservice` | Linting `style` attributes |
33
+
34
+ ```sh
35
+ npm i mathoid-texvcjs vscode-css-languageservice
36
+ ```
37
+
19
38
  ## CLI Options
20
39
 
21
40
  | Option | Argument | Description | Default |
@@ -144,6 +144,10 @@
144
144
  "105": "API talk",
145
145
  "106": "Skin",
146
146
  "107": "Skin talk",
147
+ "114": "Creation",
148
+ "115": "Creation talk",
149
+ "116": "Document",
150
+ "117": "Document talk",
147
151
  "118": "Draft",
148
152
  "119": "Draft talk",
149
153
  "126": "MOS",
@@ -375,9 +379,36 @@
375
379
  "skins": 106,
376
380
  "skin talk": 107,
377
381
  "skins talk": 107,
382
+ "creation": 114,
383
+ "创作": 114,
384
+ "萌创": 114,
385
+ "創作": 114,
386
+ "萌創": 114,
387
+ "萌娘创作": 114,
388
+ "萌娘創作": 114,
389
+ "creation talk": 115,
390
+ "萌娘创作 talk": 115,
391
+ "创作 talk": 115,
392
+ "萌创 talk": 115,
393
+ "萌娘創作 talk": 115,
394
+ "創作 talk": 115,
395
+ "萌創 talk": 115,
396
+ "萌娘创作讨论": 115,
397
+ "萌娘創作討論": 115,
398
+ "document": 116,
399
+ "library": 116,
400
+ "文档": 116,
401
+ "文檔": 116,
402
+ "document talk": 117,
403
+ "library talk": 117,
404
+ "文档 talk": 117,
405
+ "文檔 talk": 117,
406
+ "文档讨论": 117,
407
+ "文檔討論": 117,
378
408
  "draft": 118,
379
409
  "草稿": 118,
380
410
  "draft talk": 119,
411
+ "草稿 talk": 119,
381
412
  "草稿讨论": 119,
382
413
  "草稿討論": 119,
383
414
  "mos": 126,
@@ -110,6 +110,12 @@
110
110
  "13": "Help talk",
111
111
  "14": "Category",
112
112
  "15": "Category talk",
113
+ "114": "Creation",
114
+ "115": "Creation talk",
115
+ "116": "Document",
116
+ "117": "Document talk",
117
+ "118": "Draft",
118
+ "119": "Draft talk",
113
119
  "274": "Widget",
114
120
  "275": "Widget talk",
115
121
  "710": "TimedText",
@@ -138,6 +144,12 @@
138
144
  "help talk": 13,
139
145
  "category": 14,
140
146
  "category talk": 15,
147
+ "creation": 114,
148
+ "creation talk": 115,
149
+ "document": 116,
150
+ "document talk": 117,
151
+ "draft": 118,
152
+ "draft talk": 119,
141
153
  "widget": 274,
142
154
  "widget talk": 275,
143
155
  "timedtext": 710,
@@ -146,6 +158,14 @@
146
158
  "module talk": 829,
147
159
  "media": -2,
148
160
  "special": -1,
161
+ "创作": 114,
162
+ "萌创": 114,
163
+ "文档": 116,
164
+ "萌娘创作 talk": 115,
165
+ "创作 talk": 115,
166
+ "萌创 talk": 115,
167
+ "文档 talk": 117,
168
+ "草稿 talk": 119,
149
169
  "媒体": -2,
150
170
  "媒体文件": -2,
151
171
  "媒体档案": -2,
@@ -291,7 +311,25 @@
291
311
  "模組討論": 829,
292
312
  "模组对话": 829,
293
313
  "模组讨论": 829,
294
- "モジュール・トーク": 829
314
+ "モジュール・トーク": 829,
315
+ "創作": 114,
316
+ "萌創": 114,
317
+ "文檔": 116,
318
+ "萌娘創作 talk": 115,
319
+ "創作 talk": 115,
320
+ "萌創 talk": 115,
321
+ "文檔 talk": 117,
322
+ "library": 116,
323
+ "library talk": 117,
324
+ "萌娘创作": 114,
325
+ "萌娘创作讨论": 115,
326
+ "文档讨论": 117,
327
+ "草稿": 118,
328
+ "草稿讨论": 119,
329
+ "萌娘創作": 114,
330
+ "萌娘創作討論": 115,
331
+ "文檔討論": 117,
332
+ "草稿討論": 119
295
333
  },
296
334
  "functionHook": [
297
335
  "ns",
package/dist/base.d.mts CHANGED
@@ -4,14 +4,14 @@ export interface Config {
4
4
  readonly html: [string[], string[], string[]];
5
5
  readonly namespaces: Record<string, string>;
6
6
  readonly nsid: Record<string, number>;
7
- readonly variable: string[];
8
- readonly functionHook: string[];
9
7
  readonly parserFunction: [Record<string, string>, Record<string, string>, string[], string[]];
10
8
  readonly doubleUnderscore: [string[], string[], Record<string, string>, Record<string, string>];
11
9
  readonly protocol: string;
12
10
  readonly img: Record<string, string>;
13
11
  readonly redirection: string[];
14
- readonly variants: string[];
12
+ variable: string[];
13
+ functionHook: string[];
14
+ variants: string[];
15
15
  articlePath?: string;
16
16
  }
17
17
  export type ConfigData = Omit<Config, 'excludes'>;
@@ -43,7 +43,7 @@ export declare const stages: {
43
43
  converter: number;
44
44
  };
45
45
  export type Stage = keyof typeof stages;
46
- 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", "required-attr", "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"];
46
+ 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-ref", "invalid-url", "lonely-apos", "lonely-bracket", "lonely-http", "nested-link", "no-arg", "no-duplicate", "no-ignored", "obsolete-attr", "obsolete-tag", "parsing-order", "pipe-like", "required-attr", "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"];
47
47
  export declare namespace LintError {
48
48
  type Severity = 'error' | 'warning';
49
49
  type Rule = typeof rules[number];
package/dist/base.d.ts CHANGED
@@ -4,14 +4,14 @@ export interface Config {
4
4
  readonly html: [string[], string[], string[]];
5
5
  readonly namespaces: Record<string, string>;
6
6
  readonly nsid: Record<string, number>;
7
- readonly variable: string[];
8
- readonly functionHook: string[];
9
7
  readonly parserFunction: [Record<string, string>, Record<string, string>, string[], string[]];
10
8
  readonly doubleUnderscore: [string[], string[], Record<string, string>, Record<string, string>];
11
9
  readonly protocol: string;
12
10
  readonly img: Record<string, string>;
13
11
  readonly redirection: string[];
14
- readonly variants: string[];
12
+ variable: string[];
13
+ functionHook: string[];
14
+ variants: string[];
15
15
  articlePath?: string;
16
16
  }
17
17
  export type ConfigData = Omit<Config, 'excludes'>;
@@ -43,7 +43,7 @@ export declare const stages: {
43
43
  converter: number;
44
44
  };
45
45
  export type Stage = keyof typeof stages;
46
- 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", "required-attr", "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"];
46
+ 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-ref", "invalid-url", "lonely-apos", "lonely-bracket", "lonely-http", "nested-link", "no-arg", "no-duplicate", "no-ignored", "obsolete-attr", "obsolete-tag", "parsing-order", "pipe-like", "required-attr", "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"];
47
47
  export declare namespace LintError {
48
48
  type Severity = 'error' | 'warning';
49
49
  type Rule = typeof rules[number];
package/dist/base.js CHANGED
@@ -46,6 +46,7 @@ exports.rules = (() => {
46
46
  'invalid-invoke',
47
47
  'invalid-isbn',
48
48
  'invalid-json',
49
+ 'invalid-ref',
49
50
  'invalid-url',
50
51
  'lonely-apos',
51
52
  'lonely-bracket',
package/dist/base.mjs CHANGED
@@ -43,6 +43,7 @@ const rules = /* @__PURE__ */ (() => {
43
43
  "invalid-invoke",
44
44
  "invalid-isbn",
45
45
  "invalid-json",
46
+ "invalid-ref",
46
47
  "invalid-url",
47
48
  "lonely-apos",
48
49
  "lonely-bracket",
@@ -10,6 +10,7 @@ const child_process_1 = require("child_process");
10
10
  const strict_1 = __importDefault(require("assert/strict"));
11
11
  const cm_util_1 = require("@bhsd/cm-util");
12
12
  const diff_1 = require("../util/diff");
13
+ const string_1 = require("../util/string");
13
14
  /**
14
15
  * Converts an array to an object.
15
16
  * @param config parser configuration
@@ -40,7 +41,7 @@ const filterGadget = (id) => {
40
41
  const n = Number(id);
41
42
  return n < 2300 || n > 2303; // Gadget, Gadget talk, Gadget definition, Gadget definition talk
42
43
  };
43
- const pkg = "wikilint", version = "2.42.0";
44
+ const pkg = "wikilint", version = "2.44.0";
44
45
  /**
45
46
  * Get the parser configuration for a Wikimedia Foundation project.
46
47
  * @param site site nickname
@@ -108,7 +109,7 @@ exports.default = async (site, url, user, force, internal) => {
108
109
  siprop: `general|magicwords|namespaces|namespacealiases${mwConfig.functionHooks ? '' : '|functionhooks'}`,
109
110
  format: 'json',
110
111
  formatversion: '2',
111
- }, { general: { articlepath, server, variants, langconversion }, magicwords, namespaces, namespacealiases, functionhooks, } = (await (await fetch(`${url}/api.php?${new URLSearchParams(params).toString()}`, headers)).json()).query, ns = Object.entries(namespaces).filter(([id]) => filterGadget(id))
112
+ }, { general: { articlepath, server, variants, langconversion }, magicwords, namespaces, namespacealiases, functionhooks, } = (await (await fetch(`${url}/api.php?${String(new URLSearchParams(params))}`, headers)).json()).query, ns = Object.entries(namespaces).filter(([id]) => filterGadget(id))
112
113
  .flatMap(([id, { name, canonical = '' }]) => [
113
114
  [id, name],
114
115
  ...name === canonical ? [] : [[id, canonical]],
@@ -130,11 +131,11 @@ exports.default = async (site, url, user, force, internal) => {
130
131
  parserFunction[2] = getAliases(magicwords, new Set(['msg', 'raw']));
131
132
  parserFunction[3] = getAliases(magicwords, new Set(['subst', 'safesubst']));
132
133
  if (!mwConfig.functionHooks) {
133
- Object.assign(config, { functionHook: [...functionhooks.map(s => s.toLowerCase()), 'msgnw'] });
134
+ config.functionHook = [...(0, string_1.toLowerCase)(functionhooks), 'msgnw'];
134
135
  }
135
136
  if (!mwConfig.variableIDs) {
136
- const { variables } = (await (await fetch(`${url}/api.php?${new URLSearchParams({ ...params, siprop: 'variables' }).toString()}`, headers)).json()).query;
137
- Object.assign(config, { variable: [...new Set([...variables, '='])] });
137
+ const { variables } = (await (await fetch(`${url}/api.php?${String(new URLSearchParams({ ...params, siprop: 'variables' }))}`, headers)).json()).query;
138
+ config.variable = [...new Set([...variables, '='])];
138
139
  }
139
140
  if ('#choose' in parserFunction[0]) {
140
141
  delete parserFunction[0]['choose'];
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  /* eslint n/exports-style: 0 */
6
+ const cm_util_1 = require("@bhsd/cm-util");
6
7
  const base_1 = require("./base");
7
8
  const debug_1 = require("./util/debug");
8
9
  const constants_1 = require("./util/constants");
@@ -114,7 +115,8 @@ const Parser = {
114
115
  return this.getConfig();
115
116
  }
116
117
  /* NOT FOR BROWSER ONLY END */
117
- const parserConfig = config ?? this.config, { doubleUnderscore, ext, parserFunction, variable, } = parserConfig;
118
+ const parserConfig = config ?? this.config;
119
+ const { doubleUnderscore, ext, parserFunction, variable, variants, } = parserConfig;
118
120
  for (let i = 0; i < 2; i++) {
119
121
  if (doubleUnderscore[i].length === 0) {
120
122
  doubleUnderscore[i] = Object.keys(doubleUnderscore[i + 2]);
@@ -124,6 +126,7 @@ const Parser = {
124
126
  variable.push('translationlanguage');
125
127
  parserFunction[1]['TRANSLATIONLANGUAGE'] = 'translationlanguage';
126
128
  }
129
+ parserConfig.variants = (0, cm_util_1.getBCP47Variants)(variants);
127
130
  return {
128
131
  ...parserConfig,
129
132
  excludes: [],
@@ -135,19 +138,19 @@ const Parser = {
135
138
  && (this.i18n[msg] ?? msg).replace('$1', this.msg(arg));
136
139
  },
137
140
  /** @implements */
138
- normalizeTitle(title, defaultNs = 0, include, config = Parser.getConfig(), opt) {
141
+ normalizeTitle(title, defaultNs = 0, include, config = Parser.getConfig(), opt, accum = []) {
139
142
  let titleObj;
140
143
  if (opt?.halfParsed) {
141
- titleObj = new title_1.Title(title, defaultNs, config, opt);
144
+ titleObj = new title_1.Title(title, defaultNs, config, opt, accum);
142
145
  }
143
146
  else {
144
147
  const { Token } = require('./src/index');
145
148
  titleObj = debug_1.Shadow.run(() => {
146
- const root = new Token(title, config);
149
+ const root = new Token(title, config, accum);
147
150
  root.type = 'root';
148
151
  root.pageName = opt?.page;
149
152
  root.parseOnce(0, include).parseOnce();
150
- const t = new title_1.Title(root.firstChild.toString(), defaultNs, config, opt);
153
+ const t = new title_1.Title(root.firstChild.toString(), defaultNs, config, opt, accum);
151
154
  root.build();
152
155
  const keys = [
153
156
  'main',
@@ -10,23 +10,26 @@ export interface TexvcLocation {
10
10
  line: number;
11
11
  column: number;
12
12
  }
13
+ export type TexvcReport = {
14
+ status: '+';
15
+ output: string;
16
+ } | {
17
+ status: 'C';
18
+ } | {
19
+ status: 'F' | 'S';
20
+ error: {
21
+ message: string;
22
+ location: {
23
+ start: TexvcLocation;
24
+ end: TexvcLocation;
25
+ };
26
+ };
27
+ };
13
28
  declare interface Texvcjs {
14
29
  check(input: string, options?: {
30
+ usemathrm?: boolean;
15
31
  usemhchem?: boolean;
16
- }): {
17
- status: '+';
18
- } | {
19
- status: 'C';
20
- } | {
21
- status: 'F' | 'S';
22
- error: {
23
- message: string;
24
- location: {
25
- start: TexvcLocation;
26
- end: TexvcLocation;
27
- };
28
- };
29
- };
32
+ }): TexvcReport;
30
33
  }
31
34
  export declare const loadTexvcjs: () => Texvcjs | null;
32
35
  export declare const loadJsonLSP: () => JSONLanguageService | null;
@@ -7,19 +7,19 @@ exports.EmbeddedCSSDocument = exports.EmbeddedJSONDocument = exports.loadStyleli
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const common_1 = require("@bhsd/common");
9
9
  const constants_1 = require("../util/constants");
10
- let texcvjs;
10
+ let texvcjs;
11
11
  const loadTexvcjs = () => {
12
12
  NPM: {
13
- if (texcvjs === undefined) {
13
+ if (texvcjs === undefined) {
14
14
  try {
15
- texcvjs = require('mathoid-texvcjs');
15
+ texvcjs = require('mathoid-texvcjs');
16
16
  }
17
17
  catch /* c8 ignore start */ {
18
- texcvjs = null;
18
+ texvcjs = null;
19
19
  }
20
20
  /* c8 ignore stop */
21
21
  }
22
- return texcvjs;
22
+ return texvcjs;
23
23
  }
24
24
  };
25
25
  exports.loadTexvcjs = loadTexvcjs;
@@ -79,6 +79,7 @@ const defaultLintRuleConfig = {
79
79
  duplicate: 1,
80
80
  },
81
81
  ],
82
+ 'invalid-ref': [2],
82
83
  'invalid-url': [1],
83
84
  'lonely-apos': [
84
85
  1,
@@ -234,9 +235,11 @@ const defaultLintRuleConfig = {
234
235
  'void-ext': [
235
236
  2,
236
237
  {
237
- // img: 2,
238
+ // dynamicpagelist: 2,
238
239
  // languages: 2,
240
+ // rss: 2,
239
241
  // section: 2,
242
+ // templatedata: 2,
240
243
  // templatestyles: 2,
241
244
  },
242
245
  ],
@@ -1,10 +1,12 @@
1
1
  import type { Config } from '../base';
2
+ import type { Token } from '../internal';
2
3
  export interface TitleOptions {
3
4
  temporary?: boolean | undefined;
4
5
  decode?: boolean | undefined;
5
6
  selfLink?: boolean | undefined;
6
7
  halfParsed?: boolean | undefined;
7
8
  page?: string | undefined;
9
+ nowiki?: boolean;
8
10
  }
9
11
  /**
10
12
  * title object of a MediaWiki page
@@ -43,8 +45,9 @@ export declare class Title {
43
45
  * @param opt.decode 是否需要解码
44
46
  * @param opt.selfLink 是否允许selfLink
45
47
  * @param opt.page 当前页面标题
48
+ * @param opt.nowiki 是否剥离nowiki标签
46
49
  */
47
- constructor(title: string, defaultNs: number, config: Config, { temporary, decode, selfLink, page }?: TitleOptions);
50
+ constructor(title: string, defaultNs: number, config: Config, { temporary, decode, selfLink, page, nowiki }?: TitleOptions, accum?: Token[]);
48
51
  /**
49
52
  * Check if the title is a redirect and get the redirect target
50
53
  *
package/dist/lib/title.js CHANGED
@@ -74,9 +74,13 @@ class Title {
74
74
  * @param opt.decode 是否需要解码
75
75
  * @param opt.selfLink 是否允许selfLink
76
76
  * @param opt.page 当前页面标题
77
+ * @param opt.nowiki 是否剥离nowiki标签
77
78
  */
78
- constructor(title, defaultNs, config, { temporary, decode, selfLink, page } = {}) {
79
+ constructor(title, defaultNs, config, { temporary, decode, selfLink, page, nowiki } = {}, accum = []) {
79
80
  this.page = page;
81
+ if (nowiki) {
82
+ title = title.replace(/\0(\d+)e\x7F/gu, (_, p1) => accum[p1].innerText ?? '');
83
+ }
80
84
  const trimmed = title.trim(), subpage = trimmed.startsWith('../');
81
85
  if (decode && title.includes('%')) {
82
86
  try {
@@ -14,7 +14,7 @@ const attributesParent = (i = 0) => (constructor) => {
14
14
  return this.childNodes[i];
15
15
  }
16
16
  hasAttr(key) {
17
- LSP: return this.#getAttributesChild().hasAttr(key);
17
+ return this.#getAttributesChild().hasAttr(key);
18
18
  }
19
19
  getAttr(key) {
20
20
  return this.#getAttributesChild().getAttr(key);
@@ -9,7 +9,7 @@ const converter_1 = require("../src/converter");
9
9
  * @param accum
10
10
  */
11
11
  const parseConverter = (text, config, accum) => {
12
- config.regexConverter ??= new RegExp(String.raw `;(?=(?:[^;]*?=>)?\s*(?:${config.variants.join('|')})\s*:|(?:\s|\0\d+[cn]\x7F)*$)`, 'iu');
12
+ config.regexConverter ??= new RegExp(String.raw `;(?=(?:[^;]*?=>)?\s*(?:${config.variants.join('|')})\s*:|(?:\s|\0\d+[cn]\x7F)*$)`, 'u');
13
13
  const regex1 = /-\{/gu, regex2 = /-\{|\}-/gu, stack = [];
14
14
  let regex = regex1, mt = regex.exec(text);
15
15
  while (mt) {
@@ -1,3 +1,4 @@
1
+ import { BoundingRect } from '../lib/rect';
1
2
  import { Token } from './index';
2
3
  import { AtomToken } from './atom';
3
4
  import { AttributeToken } from './attribute';
@@ -144,9 +144,24 @@ class AttributesToken extends index_2.Token {
144
144
  return parentNode?.type === 'html' && parentNode.closing && this.text().trim() !== '';
145
145
  }
146
146
  /** @private */
147
+ lintRef(rect, severity) {
148
+ LINT: {
149
+ const followAttr = this.getAttrToken('follow'), follow = followAttr?.getValue(), name = this.getAttr('name'), rule = 'invalid-ref';
150
+ if (follow && !/\D/u.test(follow)) {
151
+ const msg = 'int-name';
152
+ return rect ? (0, lint_1.generateForChild)(followAttr, rect, rule, msg, severity) : msg;
153
+ }
154
+ else if (follow && (this.hasAttr('details') || name && /\D/u.test(name))) {
155
+ const msg = 'ref-follow';
156
+ return rect ? (0, lint_1.generateForSelf)(this, rect, rule, msg, severity) : msg;
157
+ }
158
+ return '';
159
+ }
160
+ }
161
+ /** @private */
147
162
  lint(start = this.getAbsoluteIndex(), re) {
148
163
  LINT: {
149
- const errors = super.lint(start, re), { parentNode, childNodes, type, name: tag } = this, attrs = new Map(), duplicated = new Set(), rect = new rect_1.BoundingRect(this, start), rules = ['no-ignored', 'required-attr', 'no-duplicate'], { lintConfig } = index_1.default, { computeEditInfo, fix } = lintConfig, s = ['closingTag', 'invalidAttributes', 'nonWordAttributes']
164
+ const errors = super.lint(start, re), { parentNode, childNodes, type, name: tag } = this, attrs = new Map(), duplicated = new Set(), rect = new rect_1.BoundingRect(this, start), rules = ['no-ignored', 'required-attr', 'invalid-ref', 'no-duplicate'], { lintConfig } = index_1.default, { computeEditInfo, fix } = lintConfig, s = ['closingTag', 'invalidAttributes', 'nonWordAttributes']
150
165
  .map(k => lintConfig.getSeverity(rules[0], k));
151
166
  if (s[0] && this.#lint()) {
152
167
  const e = (0, lint_1.generateForSelf)(this, rect, rules[0], 'attributes-of-closing-tag', s[0]);
@@ -192,12 +207,21 @@ class AttributesToken extends index_2.Token {
192
207
  }
193
208
  }
194
209
  }
195
- const severity = lintConfig.getSeverity(rules[2], 'attribute');
210
+ if (tag === 'ref') {
211
+ const severity = lintConfig.getSeverity(rules[2]);
212
+ if (severity) {
213
+ const e = this.lintRef(rect, severity);
214
+ if (e) {
215
+ errors.push(e);
216
+ }
217
+ }
218
+ }
219
+ const severity = lintConfig.getSeverity(rules[3], 'attribute');
196
220
  if (severity && duplicated.size > 0) {
197
221
  for (const key of duplicated) {
198
222
  const pairs = attrs.get(key).map(attr => [attr, attr.getValue()]);
199
223
  Array.prototype.push.apply(errors, pairs.map(([attr, value], i) => {
200
- const e = (0, lint_1.generateForChild)(attr, rect, rules[2], index_1.default.msg('duplicate-attribute', key), severity);
224
+ const e = (0, lint_1.generateForChild)(attr, rect, rules[3], index_1.default.msg('duplicate-attribute', key), severity);
201
225
  if (computeEditInfo || fix) {
202
226
  const remove = (0, lint_1.fixByRemove)(e);
203
227
  if (!value || pairs.slice(0, i).some(([, v]) => v === value)) {
@@ -39,6 +39,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.ConverterFlagsToken = void 0;
41
41
  const lint_1 = require("../util/lint");
42
+ const string_1 = require("../util/string");
42
43
  const rect_1 = require("../lib/rect");
43
44
  const gapped_1 = require("../mixin/gapped");
44
45
  const noEscape_1 = require("../mixin/noEscape");
@@ -97,20 +98,24 @@ let ConverterFlagsToken = (() => {
97
98
  getUnknownFlags() {
98
99
  return new Set(this.#flags.filter(flag => /\{{3}[^{}]+\}{3}/u.test(flag)));
99
100
  }
101
+ /** 从解析设置中获取语言变体 */
102
+ #getVariants() {
103
+ return new Set((0, string_1.toLowerCase)(this.getAttribute('config').variants));
104
+ }
100
105
  /**
101
106
  * Get language coversion flags that specify a language variant
102
107
  *
103
108
  * 获取指定语言变体的转换标记
104
109
  */
105
110
  getVariantFlags() {
106
- const variants = new Set(this.getAttribute('config').variants);
107
- return new Set(this.#flags.filter(flag => variants.has(flag)));
111
+ const variants = this.#getVariants();
112
+ return new Set((0, string_1.toLowerCase)(this.#flags).filter(flag => variants.has(flag)));
108
113
  }
109
114
  /** @private */
110
115
  // eslint-disable-next-line @typescript-eslint/class-methods-use-this
111
116
  isInvalidFlag(flag, variant, unknown) {
112
117
  return Boolean(flag)
113
- && !variant.has(flag)
118
+ && !variant.has(flag.toLowerCase())
114
119
  && !unknown.has(flag)
115
120
  && (variant.size > 0 || !definedFlags.has(flag));
116
121
  }
@@ -87,7 +87,7 @@ let ConverterRuleToken = (() => {
87
87
  constructor(rule, hasColon = true, config = index_1.default.getConfig(), accum = []) {
88
88
  super(undefined, config, accum, {});
89
89
  const i = rule.indexOf(':'), j = rule.slice(0, i).indexOf('=>'), v = j === -1 ? rule.slice(0, i) : rule.slice(j + 2, i);
90
- if (hasColon && config.variants.includes(v.trim().toLowerCase())) {
90
+ if (hasColon && config.variants.includes(v.trim())) {
91
91
  super.insertAt(new atom_1.AtomToken(v, 'converter-rule-variant', config, accum));
92
92
  super.insertAt(getRuleFromTo(rule.slice(i + 1), 'to', config, accum));
93
93
  if (j !== -1) {
@@ -1,7 +1,8 @@
1
1
  import { Token } from './index';
2
- import type { LintError, Config, TokenTypes } from '../base';
2
+ import type { LintError, Config } from '../base';
3
3
  import type { Title } from '../lib/title';
4
4
  import type { AtomToken, FileToken } from '../internal';
5
+ export type GalleryImageTypes = 'gallery-image' | 'imagemap-image';
5
6
  /**
6
7
  * image parameter
7
8
  *
@@ -26,9 +27,9 @@ export declare abstract class ImageParameterToken extends Token {
26
27
  /**
27
28
  * @param str 图片参数
28
29
  * @param extension 文件扩展名
29
- * @param type 父节点类型
30
+ * @param type 图库节点类型
30
31
  */
31
- constructor(str: string, extension: string | undefined, type: TokenTypes | undefined, config: Config, accum?: Token[]);
32
+ constructor(str: string, extension: string | undefined, type: GalleryImageTypes | undefined, config: Config, accum?: Token[]);
32
33
  /**
33
34
  * Get the parameter value
34
35
  *
@@ -62,7 +62,7 @@ const getUrl = (link) => {
62
62
  }
63
63
  return new URL(link).href;
64
64
  };
65
- function validate(key, val, config, extOrType, halfParsed) {
65
+ function validate(key, val, config, extOrType, halfParsed, accum) {
66
66
  val = (0, string_1.removeComment)(val).trim();
67
67
  let value = val.replace(key === 'link' ? /\0\d+[tq]\x7F/gu : /\0\d+t\x7F/gu, '').trim();
68
68
  switch (key) {
@@ -70,6 +70,7 @@ function validate(key, val, config, extOrType, halfParsed) {
70
70
  return !value && Boolean(val) || /^(?:\d+x?|\d*x\d+)(?:\s*px)?$/u.test(value);
71
71
  case 'link': {
72
72
  const isGalleryImage = extOrType === 'gallery-image';
73
+ let nowiki = true;
73
74
  if (!value) {
74
75
  return val;
75
76
  }
@@ -78,8 +79,9 @@ function validate(key, val, config, extOrType, halfParsed) {
78
79
  }
79
80
  else if (value.startsWith('[[') && value.endsWith(']]')) {
80
81
  value = value.slice(2, -2);
82
+ nowiki = false;
81
83
  }
82
- const title = index_1.default.normalizeTitle(value, 0, false, config, { halfParsed, decode: true, selfLink: true, page: '' });
84
+ const title = index_1.default.normalizeTitle(value, 0, false, config, { halfParsed, decode: true, selfLink: true, page: '', nowiki }, accum);
83
85
  return title.valid ? title : isGalleryImage;
84
86
  }
85
87
  case 'lang':
@@ -137,12 +139,12 @@ let ImageParameterToken = (() => {
137
139
  }
138
140
  const value = super.text().trim();
139
141
  return debug_1.Shadow.run(() => {
140
- const config = this.getAttribute('config'), token = new index_2.Token(value, config);
142
+ const config = this.getAttribute('config'), accum = [], token = new index_2.Token(value, config, accum);
141
143
  token.parseOnce(0, this.getAttribute('include')).parseOnce();
142
144
  if (/^\0\d+m\x7F/u.test(token.firstChild.toString())) {
143
145
  return value;
144
146
  }
145
- const link = validate('link', value, config, this.parentNode?.type);
147
+ const link = validate('link', value, config, this.parentNode?.type, undefined, accum);
146
148
  return link === true ? undefined : link;
147
149
  }, index_1.default);
148
150
  }
@@ -150,7 +152,7 @@ let ImageParameterToken = (() => {
150
152
  /**
151
153
  * @param str 图片参数
152
154
  * @param extension 文件扩展名
153
- * @param type 父节点类型
155
+ * @param type 图库节点类型
154
156
  */
155
157
  constructor(str, extension, type, config, accum) {
156
158
  let mt;
@@ -159,7 +161,7 @@ let ImageParameterToken = (() => {
159
161
  mt = regex.exec(str);
160
162
  return mt
161
163
  && (mt.length !== 4
162
- || validate(key, mt[2], config, key === 'link' ? type : extension, true) !== false);
164
+ || validate(key, mt[2], config, key === 'link' ? type : extension, true, accum) !== false);
163
165
  });
164
166
  // @ts-expect-error mt already assigned
165
167
  if (param && mt) {
@@ -285,7 +287,13 @@ let ImageParameterToken = (() => {
285
287
  * 获取参数值
286
288
  */
287
289
  getValue() {
288
- LINT: return this.name === 'invalid' ? this.text() : this.#isVoid() || super.text();
290
+ LINT: {
291
+ const { name, childNodes } = this;
292
+ if (name === 'link') {
293
+ return (0, string_1.text)(childNodes.map(child => child.is('ext') && child.name === 'nowiki' ? child.innerText ?? '' : child));
294
+ }
295
+ return name === 'invalid' ? this.text() : this.#isVoid() || super.text();
296
+ }
289
297
  }
290
298
  };
291
299
  return ImageParameterToken = _classThis;
@@ -1,6 +1,7 @@
1
1
  import { LinkBaseToken } from './base';
2
2
  import { ImageParameterToken } from '../imageParameter';
3
- import type { TokenTypes, Config, LintError } from '../../base';
3
+ import type { Config, LintError } from '../../base';
4
+ import type { GalleryImageTypes } from '../imageParameter';
4
5
  import type { Token, AtomToken } from '../../internal';
5
6
  /**
6
7
  * image
@@ -11,7 +12,7 @@ import type { Token, AtomToken } from '../../internal';
11
12
  export declare abstract class FileToken extends LinkBaseToken {
12
13
  readonly childNodes: readonly [AtomToken, ...ImageParameterToken[]];
13
14
  abstract get lastChild(): AtomToken | ImageParameterToken;
14
- get type(): 'file' | 'gallery-image' | 'imagemap-image';
15
+ get type(): 'file' | GalleryImageTypes;
15
16
  /**
16
17
  * file extension
17
18
  *
@@ -23,9 +24,9 @@ export declare abstract class FileToken extends LinkBaseToken {
23
24
  * @param link 文件名
24
25
  * @param text 图片参数
25
26
  * @param delimiter `|`
26
- * @param type 节点类型
27
+ * @param type 图库节点类型
27
28
  */
28
- constructor(link: string, text?: string, config?: Config, accum?: Token[], delimiter?: string, type?: TokenTypes);
29
+ constructor(link: string, text?: string, config?: Config, accum?: Token[], delimiter?: string, type?: GalleryImageTypes);
29
30
  /**
30
31
  * Get all image parameter tokens
31
32
  *
@@ -69,7 +69,7 @@ class FileToken extends base_1.LinkBaseToken {
69
69
  * @param link 文件名
70
70
  * @param text 图片参数
71
71
  * @param delimiter `|`
72
- * @param type 节点类型
72
+ * @param type 图库节点类型
73
73
  */
74
74
  constructor(link, text, config, accum = [], delimiter = '|', type) {
75
75
  super(link, undefined, config, accum, delimiter);
@@ -1,6 +1,7 @@
1
1
  import { NowikiBaseToken } from './base';
2
2
  import type { LintError } from '../../base';
3
3
  import type { AttributesToken, ExtToken } from '../../internal';
4
+ import type { TexvcReport } from '../../lib/document';
4
5
  /**
5
6
  * text-only token inside an extension tag
6
7
  *
@@ -44,9 +44,7 @@ class NowikiToken extends base_1.NowikiBaseToken {
44
44
  /** @private */
45
45
  lint(start = this.getAbsoluteIndex()) {
46
46
  LINT: {
47
- const { name, innerText,
48
- /* NOT FOR BROWSER ONLY */
49
- previousSibling, } = this, { lintConfig } = index_1.default;
47
+ const { name, innerText } = this, { lintConfig } = index_1.default;
50
48
  let rule = 'void-ext', s = lintConfig.getSeverity(rule, name);
51
49
  if (s && this.#lint()) {
52
50
  const e = (0, lint_1.generateForSelf)(this, { start }, rule, index_1.default.msg('nothing-in', name), s);
@@ -82,31 +80,9 @@ class NowikiToken extends base_1.NowikiBaseToken {
82
80
  rule = 'invalid-math';
83
81
  s = lintConfig.getSeverity(rule);
84
82
  if (s && constants_2.mathTags.has(name)) {
85
- const texvcjs = (0, document_1.loadTexvcjs)();
86
- if (texvcjs) {
87
- const isChem = name !== 'math', display = previousSibling?.getAttr('display') ?? 'block';
88
- let tex = innerText, n = 0;
89
- if (isChem) {
90
- tex = String.raw `\ce{${tex}}`;
91
- n = 4;
92
- }
93
- switch (display) {
94
- case 'block':
95
- tex = String.raw `{\displaystyle ${tex}}`;
96
- n += 15;
97
- break;
98
- case 'inline':
99
- tex = String.raw `{\textstyle ${tex}}`;
100
- n += 12;
101
- break;
102
- case 'linebreak':
103
- tex = String.raw `\[ ${tex} \]`;
104
- n += 3;
105
- // no default
106
- }
107
- const result = texvcjs.check(tex, {
108
- usemhchem: isChem || Boolean(previousSibling?.hasAttr('chem')),
109
- });
83
+ const report = this.texvcCheck();
84
+ if (report) {
85
+ const [result, n] = report;
110
86
  if (result.status === '+') {
111
87
  return [];
112
88
  }
@@ -125,5 +101,40 @@ class NowikiToken extends base_1.NowikiBaseToken {
125
101
  return super.lint(start, getLintRegex(name));
126
102
  }
127
103
  }
104
+ /* NOT FOR BROWSER ONLY */
105
+ /** @private */
106
+ texvcCheck() {
107
+ const texvcjs = (0, document_1.loadTexvcjs)();
108
+ if (!texvcjs) {
109
+ return undefined;
110
+ }
111
+ let tex = this.innerText, n = 0;
112
+ if (this.name !== 'math') {
113
+ tex = String.raw `\ce{${tex}}`;
114
+ n = 4;
115
+ }
116
+ switch (this.previousSibling?.getAttr('display') ?? 'block') {
117
+ case 'block':
118
+ tex = String.raw `{\displaystyle ${tex}}`;
119
+ n += 15;
120
+ break;
121
+ case 'inline':
122
+ tex = String.raw `{\textstyle ${tex}}`;
123
+ n += 12;
124
+ break;
125
+ case 'linebreak':
126
+ tex = String.raw `\[ ${tex} \]`;
127
+ n += 3;
128
+ // no default
129
+ }
130
+ return [
131
+ texvcjs.check(tex, {
132
+ usemathrm: true,
133
+ usemhchem: this.name !== 'math' || Boolean(this.previousSibling?.hasAttr('chem')),
134
+ }),
135
+ n,
136
+ tex,
137
+ ];
138
+ }
128
139
  }
129
140
  exports.NowikiToken = NowikiToken;
@@ -1,3 +1,4 @@
1
+ import { BoundingRect } from '../../lib/rect';
1
2
  import Parser from '../../index';
2
3
  import { Token } from '../index';
3
4
  import { TagPairToken } from './index';
@@ -45,6 +45,7 @@ const attributesParent_1 = require("../../mixin/attributesParent");
45
45
  const index_2 = require("../index");
46
46
  const index_3 = require("./index");
47
47
  const attributes_1 = require("../attributes");
48
+ const nonVoidExt = new Set(['dynamicpagelist', 'templatedata', 'rss']);
48
49
  /**
49
50
  * extension tag
50
51
  *
@@ -202,16 +203,35 @@ let ExtToken = (() => {
202
203
  super(name, attrToken, innerToken, closed, config, accum);
203
204
  }
204
205
  /** @private */
206
+ lintRef(rect, severity) {
207
+ LINT: {
208
+ const refName = this.getAttr('name'), text = this.innerText?.trim();
209
+ let msg = '';
210
+ if (this.closest('ext#references')) {
211
+ if (!refName) {
212
+ msg = 'no-named-in-references';
213
+ }
214
+ else if (!text) {
215
+ msg = 'no-empty-in-references';
216
+ }
217
+ }
218
+ else if (!refName && !text) {
219
+ msg = this.hasAttr('details') ? 'ref-details' : 'ref-no-name';
220
+ }
221
+ return msg && (rect ? (0, lint_1.generateForSelf)(this, rect, 'invalid-ref', msg, severity) : msg);
222
+ }
223
+ }
224
+ /** @private */
205
225
  lint(start = this.getAbsoluteIndex(), re) {
206
226
  LINT: {
207
- const errors = super.lint(start, re), { lintConfig } = index_1.default, rect = new rect_1.BoundingRect(this, start);
208
- if (this.name !== 'nowiki') {
227
+ const errors = super.lint(start, re), { name } = this, { lintConfig } = index_1.default, rect = new rect_1.BoundingRect(this, start);
228
+ if (name !== 'nowiki') {
209
229
  const s = this.inHtmlAttrs(), rule = 'parsing-order', severity = s && lintConfig.getSeverity(rule, s === 2 ? 'ext' : 'templateInTable');
210
230
  if (severity) {
211
231
  errors.push((0, lint_1.generateForSelf)(this, rect, rule, 'ext-in-html', severity));
212
232
  }
213
233
  }
214
- if (this.name === 'ref') {
234
+ if (name === 'ref') {
215
235
  let rule = 'var-anchor', s = lintConfig.getSeverity(rule, 'ref');
216
236
  if (s && this.closest('heading-title')) {
217
237
  errors.push((0, lint_1.generateForSelf)(this, rect, rule, 'variable-anchor', s));
@@ -221,6 +241,19 @@ let ExtToken = (() => {
221
241
  if (s && this.closest('link-text,ext-link-text')) {
222
242
  errors.push((0, lint_1.generateForSelf)(this, rect, rule, 'ref-in-link', s));
223
243
  }
244
+ s = lintConfig.getSeverity('invalid-ref');
245
+ if (s) {
246
+ const e = this.lintRef(rect, s);
247
+ if (e) {
248
+ errors.push(e);
249
+ }
250
+ }
251
+ }
252
+ else {
253
+ const rule = 'void-ext', s = lintConfig.getSeverity(rule, name);
254
+ if (s && nonVoidExt.has(name) && !this.innerText) {
255
+ errors.push((0, lint_1.generateForSelf)(this, { start }, rule, index_1.default.msg('no-empty', name), s));
256
+ }
224
257
  }
225
258
  return errors;
226
259
  }
@@ -388,7 +388,7 @@ let TranscludeToken = (() => {
388
388
  * @throws `Error` 不是可接受的魔术字
389
389
  */
390
390
  getPossibleValues() {
391
- const { type, name, childNodes } = this;
391
+ const { type, name, childNodes, length: l } = this;
392
392
  if (type === 'template') {
393
393
  throw new Error('TranscludeToken.getPossibleValues method is only for specific magic words!');
394
394
  }
@@ -397,12 +397,14 @@ let TranscludeToken = (() => {
397
397
  case 'if':
398
398
  case 'ifexist':
399
399
  case 'ifexpr':
400
- case 'iferror':
401
400
  start = 2;
402
401
  break;
403
402
  case 'ifeq':
404
403
  start = 3;
405
404
  break;
405
+ case 'iferror':
406
+ start = l > 3 ? 2 : 1;
407
+ break;
406
408
  case 'switch': {
407
409
  const parameters = childNodes.slice(2), last = parameters[parameters.length - 1];
408
410
  queue = [
@@ -1,16 +1,34 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const binary_search_1 = __importDefault(require("binary-search"));
7
3
  /**
8
4
  * 二分法查找索引
9
5
  * @param haystack 数组
10
6
  * @param needle 目标值
11
7
  * @param comparator 比较函数
8
+ * @author The Dark Sky Company, LLC <support@darkskyapp.com>
9
+ * @license CC0-1.0
10
+ * @see https://github.com/darkskyapp/binary-search/blob/master/index.js
12
11
  */
13
12
  exports.default = (haystack, needle, comparator) => {
14
- const found = (0, binary_search_1.default)(haystack, needle, comparator);
15
- return found < 0 ? ~found : found; // eslint-disable-line no-bitwise
13
+ let low = 0, high = haystack.length - 1;
14
+ while (low <= high) {
15
+ // The naive `low + high >>> 1` could fail for array lengths > 2**31
16
+ // because `>>>` converts its operands to int32. `low + (high - low >>> 1)`
17
+ // works for array lengths <= 2**32-1 which is also Javascript's max array
18
+ // length.
19
+ const mid = low + (high - low >>> 1), // eslint-disable-line no-bitwise
20
+ cmp = comparator(haystack[mid], needle);
21
+ if (cmp < 0) {
22
+ low = mid + 1;
23
+ }
24
+ else if (cmp > 0) {
25
+ high = mid - 1;
26
+ }
27
+ else {
28
+ // Key found.
29
+ return mid;
30
+ }
31
+ }
32
+ // Key not found.
33
+ return low;
16
34
  };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.decodeNumber = exports.decodeHtml = exports.decodeHtmlBasic = exports.text = exports.removeComment = exports.tidy = exports.trimLc = exports.extUrlChar = exports.extUrlCharFirst = exports.zs = void 0;
3
+ exports.toLowerCase = exports.decodeNumber = exports.decodeHtml = exports.decodeHtmlBasic = exports.text = exports.removeComment = exports.tidy = exports.trimLc = exports.extUrlChar = exports.extUrlCharFirst = exports.zs = void 0;
4
4
  exports.restore = restore;
5
5
  exports.zs = String.raw ` \xA0\u1680\u2000-\u200A\u202F\u205F\u3000`;
6
6
  const commonExtUrlChar = String.raw `[^[\]<>"\0-\x1F\x7F${exports.zs}\uFFFD]`;
@@ -68,3 +68,9 @@ const decodeHtml = (str) => {
68
68
  exports.decodeHtml = decodeHtml;
69
69
  /** decode numbered HTML entities */
70
70
  exports.decodeNumber = factory(/&#(\d+|x[\da-f]+);/giu, (_, code) => String.fromCodePoint(Number((/^x/iu.test(code) ? '0' : '') + code)));
71
+ /**
72
+ * Convert an array of strings to lowercase
73
+ * @param str array of strings
74
+ */
75
+ const toLowerCase = (str) => str.map(s => s.toLowerCase());
76
+ exports.toLowerCase = toLowerCase;
package/i18n/en.json CHANGED
@@ -34,6 +34,7 @@
34
34
  "in-url": "$1 in URL",
35
35
  "inconsistent-table": "inconsistent table layout",
36
36
  "insecure-style": "insecure style",
37
+ "int-name": "name cannot be a simple integer",
37
38
  "invalid-attribute": "element containing an invalid attribute name",
38
39
  "invalid-category": "invalid category name",
39
40
  "invalid-content": "invalid content in <$1>",
@@ -55,6 +56,9 @@
55
56
  "missing-extension": "missing file extension",
56
57
  "missing-function": "missing Scribunto module function name",
57
58
  "newline": null,
59
+ "no-empty": "<$1> should not be empty",
60
+ "no-empty-in-references": "<ref> tag defined in <references> has no content",
61
+ "no-named-in-references": "<ref> tag defined in <references> has no name attribute",
58
62
  "no-self-closing": "no self-closing",
59
63
  "nonzero-tabindex": "nonzero tabindex",
60
64
  "nothing-in": "nothing should be in <$1>",
@@ -65,7 +69,10 @@
65
69
  "pipe-in-link": "additional \"|\" in the link text",
66
70
  "pipe-in-table": "additional \"|\" in a table cell",
67
71
  "redirect-like": "redirect-like syntax in a list item",
72
+ "ref-details": "a <ref> tag with details must contain content or point to a parent reference by name",
73
+ "ref-follow": "a <ref follow> tag that is the continuation of a previous one cannot be named individually or have details",
68
74
  "ref-in-link": "<ref> in an internal or external link",
75
+ "ref-no-name": "<ref> with no name must have content",
69
76
  "remove": null,
70
77
  "required-attribute": "required $1 attribute is missing",
71
78
  "template-in-link": "template in an internal link target",
package/i18n/zh-hans.json CHANGED
@@ -33,6 +33,7 @@
33
33
  "imagemap-without-image": "没有图像的<imagemap>",
34
34
  "in-url": "URL中的$1",
35
35
  "inconsistent-table": "不一致的表格布局",
36
+ "int-name": "name不能是单纯整数",
36
37
  "insecure-style": "不安全的样式",
37
38
  "invalid-attribute": "包含无效属性名称的元素",
38
39
  "invalid-category": "无效的分类名",
@@ -55,6 +56,9 @@
55
56
  "missing-extension": "缺少文件扩展名",
56
57
  "missing-function": "缺少Scribunto模块函数名",
57
58
  "newline": "换行",
59
+ "no-empty": "<$1>不应为空",
60
+ "no-empty-in-references": "<references>内定义的<ref>标签没有内容",
61
+ "no-named-in-references": "<references>内定义的<ref>标签没有设置name属性",
58
62
  "no-self-closing": "移除自封闭",
59
63
  "nonzero-tabindex": "非零的tabindex",
60
64
  "nothing-in": "<$1>不应有任何内容",
@@ -65,7 +69,10 @@
65
69
  "pipe-in-link": "链接文本中的额外\"|\"",
66
70
  "pipe-in-table": "表格单元格中的额外\"|\"",
67
71
  "redirect-like": "列表项中的疑似重定向语法",
72
+ "ref-details": "带有详细信息的<ref>标签必须包含内容或通过名称指向父级参考",
73
+ "ref-follow": "作为前一个标签的延续的<ref follow>标签不能被单独命名或包含详细信息",
68
74
  "ref-in-link": "内部或外部链接中的<ref>",
75
+ "ref-no-name": "没有name的<ref>必须有内容",
69
76
  "remove": "移除",
70
77
  "required-attribute": "缺少必需的$1属性",
71
78
  "template-in-link": "内部链接目标中的模板",
package/i18n/zh-hant.json CHANGED
@@ -33,6 +33,7 @@
33
33
  "imagemap-without-image": "<imagemap>沒有圖片",
34
34
  "in-url": "URL中有$1",
35
35
  "inconsistent-table": "表格版面不一致",
36
+ "int-name": "name屬性不能使用數字",
36
37
  "insecure-style": "不安全的樣式",
37
38
  "invalid-attribute": "包含無效屬性名稱的元素",
38
39
  "invalid-category": "無效的分類名",
@@ -55,6 +56,9 @@
55
56
  "missing-extension": "缺少副檔名",
56
57
  "missing-function": "缺少Scribunto模組函式名稱",
57
58
  "newline": "換行",
59
+ "no-empty": "<$1>不應為空",
60
+ "no-empty-in-references": "<references>內定義的<ref>標籤沒有內容",
61
+ "no-named-in-references": "<references> 中定義的 <ref> 沒有設定 name 屬性",
58
62
  "no-self-closing": "移除自封閉",
59
63
  "nonzero-tabindex": "非零的tabindex",
60
64
  "nothing-in": "<$1>不應有任何內容",
@@ -65,7 +69,10 @@
65
69
  "pipe-in-link": "連結文字有額外的\"|\"字元",
66
70
  "pipe-in-table": "表格儲存格有額外的\"|\"字元",
67
71
  "redirect-like": "列表項中的疑似重新導向語法",
72
+ "ref-details": "一個帶有詳細資訊的 <ref> 標籤必須包含內容、或透過名稱指向父級參考",
73
+ "ref-follow": "作為前一個延續的 <ref follow> 標籤,不能單獨命名或包含詳細資訊",
68
74
  "ref-in-link": "內部或外部連結裡的<ref>",
75
+ "ref-no-name": "沒有名稱的<ref>必須要有內容",
69
76
  "remove": "移除",
70
77
  "required-attribute": "缺少必需的$1屬性",
71
78
  "template-in-link": "內部連結目標中的模板",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikilint",
3
- "version": "2.42.0",
3
+ "version": "2.44.0",
4
4
  "description": "A Node.js linter for MediaWiki markup",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -39,7 +39,7 @@
39
39
  "url": "git+https://github.com/bhsd-harry/wikiparser-node.git"
40
40
  },
41
41
  "scripts": {
42
- "ls": "npm i --package-lock-only && npm ls --package-lock-only --all --omit=dev",
42
+ "ls": "npm i --package-lock-only && npm ls --package-lock-only --all --include=peer --omit=dev",
43
43
  "prepublishOnly": "npm run build:core",
44
44
  "build:core": "bash build.sh",
45
45
  "build:define": "bash sed.sh -i \"s/pkg = \\$PKG/pkg = $(npm pkg get name)/; s/version = \\$VERSION/version = $(npm pkg get version)/\" dist/bin/config.js",
@@ -73,40 +73,62 @@
73
73
  "json-summary"
74
74
  ]
75
75
  },
76
+ "peerDependencies": {
77
+ "color-name": ">=2.0.0",
78
+ "mathoid-texvcjs": ">=0.4.0",
79
+ "stylelint": ">=16.0.0",
80
+ "vscode-css-languageservice": ">=3.0.0",
81
+ "vscode-html-languageservice": ">=3.1.0",
82
+ "vscode-json-languageservice": ">=3.2.0"
83
+ },
84
+ "peerDependenciesMeta": {
85
+ "color-name": {
86
+ "optional": true
87
+ },
88
+ "mathoid-texvcjs": {
89
+ "optional": true
90
+ },
91
+ "stylelint": {
92
+ "optional": true
93
+ },
94
+ "vscode-css-languageservice": {
95
+ "optional": true
96
+ },
97
+ "vscode-html-languageservice": {
98
+ "optional": true
99
+ },
100
+ "vscode-json-languageservice": {
101
+ "optional": true
102
+ }
103
+ },
76
104
  "dependencies": {
77
- "@bhsd/cm-util": "^2.0.1",
105
+ "@bhsd/cm-util": "^2.2.0",
78
106
  "@bhsd/common": "^2.2.1",
79
- "@bhsd/stylelint-util": "^1.0.3",
80
- "binary-search": "^1.3.6",
81
- "vscode-languageserver-types": "^3.17.5"
82
- },
83
- "optionalDependencies": {
84
- "color-name": "~2.0.2",
107
+ "@bhsd/stylelint-util": "^1.1.0",
85
108
  "entities": "^8.0.0",
86
- "mathoid-texvcjs": "^0.6.0",
87
- "stylelint": "^17.12.0",
88
- "vscode-css-languageservice": "^6.3.10",
89
- "vscode-html-languageservice": "^5.6.2",
90
- "vscode-json-languageservice": "^5.7.2"
109
+ "vscode-languageserver-types": "^3.17.5"
91
110
  },
92
111
  "devDependencies": {
93
- "@bhsd/code-standard": "^2.5.1",
112
+ "@bhsd/code-standard": "^2.6.2",
94
113
  "@bhsd/nodejs": "^1.0.1",
95
- "@bhsd/test-util": "^1.3.1",
114
+ "@bhsd/test-util": "^1.5.0",
96
115
  "@types/color-name": "^2.0.0",
97
116
  "@types/color-rgba": "^2.1.3",
98
- "@types/mocha": "^10.0.10",
99
- "@types/node": "^25.9.0",
100
- "@typescript-eslint/parser": "^8.59.4",
101
117
  "c8": "^11.0.0",
118
+ "color-name": "~2.0.2",
102
119
  "color-rgba": "^3.0.0",
103
120
  "diff2html-cli": "^5.2.15",
104
121
  "esbuild": "^0.28.0",
105
122
  "eslint": "^10.4.1",
106
123
  "markdownlint-cli2": "^0.22.1",
124
+ "mathoid-texvcjs": "^0.6.0",
107
125
  "mocha": "^11.7.6",
126
+ "stylelint": "^17.13.0",
108
127
  "typescript": "^6.0.3",
109
128
  "v8r": "^6.1.0",
129
+ "vscode-css-languageservice": "^6.3.10",
130
+ "vscode-html-languageservice": "^5.6.2",
131
+ "vscode-json-languageservice": "^5.7.2",
110
132
  "vscode-languageserver-textdocument": "^1.0.12"
111
133
  },
112
134
  "engines": {