wikilint 2.46.2 → 2.46.4

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
@@ -40,6 +40,7 @@ npm i mathoid-texvcjs vscode-css-languageservice
40
40
 
41
41
  | Option | Argument | Description | Default |
42
42
  | :----- | :------- | :---------- | :------ |
43
+ | `--` | | End of options | |
43
44
  | `-c`, `--config` | \<path or preset config\> | Choose parser's [configuration](https://github.com/bhsd-harry/wikiparser-node/wiki/Parser-%28EN%29#config) | `default` |
44
45
  | `--cache` | | Enable caching | `false` |
45
46
  | `--cache-file` | \<path\> | Specify cache file and enable caching | `.wikilintcache` |
@@ -74,3 +75,20 @@ The generated configuration file will be saved in the `config` directory. You ca
74
75
  # For example:
75
76
  npx wikilint --config frwiki *.wiki
76
77
  ```
78
+
79
+ ## Use with pre-commit
80
+
81
+ To use `wikilint` with [pre-commit](https://pre-commit.com/), add the following to your [`.pre-commit-config.yaml`](https://pre-commit.com/#adding-pre-commit-plugins-to-your-project):
82
+
83
+ ```yaml
84
+ repos:
85
+ - repo: local
86
+ hooks:
87
+ - id: wikilint
88
+ name: WikiLint
89
+ # You can add any CLI options as needed
90
+ entry: wikilint
91
+ language: node
92
+ additional_dependencies: ['wikilint@2.46.4']
93
+ files: \.(mediawiki|wiki|wikitext)$
94
+ ```
@@ -603,6 +603,7 @@
603
603
  "chart",
604
604
  "avatar",
605
605
  "img",
606
+ "uic",
606
607
  "mentor"
607
608
  ],
608
609
  "variable": [
@@ -953,6 +954,8 @@
953
954
  "#lstx": "lstx",
954
955
  "#section-x": "lstx",
955
956
  "#assessment": "assessment",
957
+ "#uic": "uic",
958
+ "#userinfocard": "uic",
956
959
  "#mentor": "mentor",
957
960
  "#property": "property",
958
961
  "#屬性": "property",
@@ -1449,6 +1452,7 @@
1449
1452
  "nomediaviewercarousel": "nomediaviewercarousel"
1450
1453
  },
1451
1454
  {
1455
+ "EXPECTSHORTPAGE": "EXPECTSHORTPAGE",
1452
1456
  "EXPECTUNUSEDCATEGORY": "EXPECTUNUSEDCATEGORY",
1453
1457
  "DISAMBIG": "DISAMBIGUATION",
1454
1458
  "消除歧义": "DISAMBIGUATION",
@@ -280,6 +280,7 @@
280
280
  "commaseparatedlist",
281
281
  "assessment",
282
282
  "chart",
283
+ "uic",
283
284
  "mentor",
284
285
  "msgnw"
285
286
  ],
@@ -436,6 +437,8 @@
436
437
  "#commaseparatedlist": "commaseparatedlist",
437
438
  "#assessment": "assessment",
438
439
  "#chart": "chart",
440
+ "#uic": "uic",
441
+ "#userinfocard": "uic",
439
442
  "#mentor": "mentor",
440
443
  "articlepath": "articlepath",
441
444
  "scriptpath": "scriptpath",
@@ -578,6 +581,7 @@
578
581
  "notalk": "notalk"
579
582
  },
580
583
  {
584
+ "EXPECTSHORTPAGE": "EXPECTSHORTPAGE",
581
585
  "EXPECTUNUSEDCATEGORY": "EXPECTUNUSEDCATEGORY",
582
586
  "EXPECTUNUSEDTEMPLATE": "EXPECTUNUSEDTEMPLATE",
583
587
  "HIDDENCAT": "HIDDENCAT",
@@ -306,6 +306,7 @@
306
306
  "statements",
307
307
  "commaseparatedlist",
308
308
  "chart",
309
+ "uic",
309
310
  "mentor",
310
311
  "msgnw"
311
312
  ],
@@ -510,6 +511,8 @@
510
511
  "#statements": "statements",
511
512
  "#commaseparatedlist": "commaseparatedlist",
512
513
  "#chart": "chart",
514
+ "#uic": "uic",
515
+ "#userinfocard": "uic",
513
516
  "#mentor": "mentor",
514
517
  "記事パス": "articlepath",
515
518
  "articlepath": "articlepath",
@@ -863,6 +866,7 @@
863
866
  "notalk": "notalk"
864
867
  },
865
868
  {
869
+ "EXPECTSHORTPAGE": "EXPECTSHORTPAGE",
866
870
  "EXPECTUNUSEDCATEGORY": "EXPECTUNUSEDCATEGORY",
867
871
  "EXPECTUNUSEDTEMPLATE": "EXPECTUNUSEDTEMPLATE",
868
872
  "カテゴリ非表示": "HIDDENCAT",
@@ -419,6 +419,7 @@
419
419
  "commaseparatedlist",
420
420
  "assessment",
421
421
  "chart",
422
+ "uic",
422
423
  "mentor",
423
424
  "msgnw"
424
425
  ],
@@ -649,6 +650,8 @@
649
650
  "#commaseparatedlist": "commaseparatedlist",
650
651
  "#assessment": "assessment",
651
652
  "#chart": "chart",
653
+ "#uic": "uic",
654
+ "#userinfocard": "uic",
652
655
  "#mentor": "mentor",
653
656
  "articlepath": "articlepath",
654
657
  "条目路径": "articlepath",
@@ -938,6 +941,7 @@
938
941
  "notalk": "notalk"
939
942
  },
940
943
  {
944
+ "EXPECTSHORTPAGE": "EXPECTSHORTPAGE",
941
945
  "EXPECTUNUSEDCATEGORY": "EXPECTUNUSEDCATEGORY",
942
946
  "EXPECTUNUSEDTEMPLATE": "EXPECTUNUSEDTEMPLATE",
943
947
  "HIDDENCAT": "HIDDENCAT",
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="98" height="20" role="img" aria-label="Coverage: 96%"><title>Coverage: 96%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="98" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="35" height="20" fill="#4c1"/><rect width="98" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="795" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="250">96%</text><text x="795" y="140" transform="scale(.1)" fill="#fff" textLength="250">96%</text></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="98" height="20" aria-label="Coverage: 95%"><title>Coverage: 95%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="98" height="20" fill="#fff" rx="3"/></clipPath><g clip-path="url(#r)"><path fill="#555" d="M0 0h63v20H0z"/><path fill="#4c1" d="M63 0h35v20H63z"/><path fill="url(#s)" d="M0 0h98v20H0z"/></g><g fill="#fff" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="110" text-anchor="middle" text-rendering="geometricPrecision"><text x="325" y="150" fill="#010101" fill-opacity=".3" aria-hidden="true" textLength="530" transform="scale(.1)">Coverage</text><text x="325" y="140" textLength="530" transform="scale(.1)">Coverage</text><text x="795" y="150" fill="#010101" fill-opacity=".3" aria-hidden="true" textLength="250" transform="scale(.1)">95%</text><text x="795" y="140" textLength="250" transform="scale(.1)">95%</text></g></svg>
@@ -52,7 +52,7 @@
52
52
  "aliases": [
53
53
  "expectunusedcategory"
54
54
  ],
55
- "description": "Used on a category page, removes the category from Special:UnusedCategories."
55
+ "description": "Used on a category page, removes the category from [Special:UnusedCategories](https://www.mediawiki.org/wiki/Special:UnusedCategories)."
56
56
  },
57
57
  {
58
58
  "aliases": [
@@ -90,7 +90,13 @@
90
90
  "aliases": [
91
91
  "expectunusedtemplate"
92
92
  ],
93
- "description": "Used on a template page, removes the page from Special:UnusedTemplates."
93
+ "description": "Used on a template page, removes the page from [Special:UnusedTemplates](https://www.mediawiki.org/wiki/Special:UnusedTemplates)."
94
+ },
95
+ {
96
+ "aliases": [
97
+ "expectshortpage"
98
+ ],
99
+ "description": "Used on a article page, removes the page from [Special:ShortPages](https://www.mediawiki.org/wiki/Special:ShortPages)."
94
100
  },
95
101
  {
96
102
  "aliases": [
@@ -702,7 +708,7 @@
702
708
  }
703
709
  ]
704
710
  ],
705
- "description": "A switch for the gender set by the user in Special:Preferences.\n\nIf only two parameters are used (for male and female text) but the 3rd parameter is omitted, and user hasn't defined their gender, then the first parameter (`male text`) is returned as a default.\n\nA blank username field (`{{GENDER:|`) means the current user, but can be used only in interface messages (MediaWiki namespace).\n\nUsing a dot (`.`) in the username field produces the output corresponding to the default user gender on this wiki."
711
+ "description": "A switch for the gender set by the user in [Special:Preferences](https://www.mediawiki.org/wiki/Special:Preferences).\n\nIf only two parameters are used (for male and female text) but the 3rd parameter is omitted, and user hasn't defined their gender, then the first parameter (`male text`) is returned as a default.\n\nA blank username field (`{{GENDER:|`) means the current user, but can be used only in interface messages (MediaWiki namespace).\n\nUsing a dot (`.`) in the username field produces the output corresponding to the default user gender on this wiki."
706
712
  },
707
713
  {
708
714
  "aliases": [
@@ -958,7 +964,7 @@
958
964
  }
959
965
  ]
960
966
  ],
961
- "description": "**[Expensive]** This function takes an input string, interprets it as a page title, and returns one of two values depending on whether or not the page exists on the local wiki.\n\nThe function evaluates to `true` for [system messages](https://www.mediawiki.org/wiki/Help:System_message) that have been customized, and for [special pages](https://www.mediawiki.org/wiki/Help:Special_pages) that are defined by the software.\n\nIf a page checks a target using `#ifexist:`, then that page will appear in the Special:WhatLinksHere list for the target page.\n\n`#ifexist:` does not work with interwiki links.\n\nA page that does not exist and is tested for using #ifexist will end up on the [Wanted Pages](https://www.mediawiki.org/wiki/Manual:WantedPages)."
967
+ "description": "**[Expensive]** This function takes an input string, interprets it as a page title, and returns one of two values depending on whether or not the page exists on the local wiki.\n\nThe function evaluates to `true` for [system messages](https://www.mediawiki.org/wiki/Help:System_message) that have been customized, and for [special pages](https://www.mediawiki.org/wiki/Help:Special_pages) that are defined by the software.\n\nIf a page checks a target using `#ifexist:`, then that page will appear in the [Special:WhatLinksHere](https://www.mediawiki.org/wiki/Special:WhatLinksHere) list for the target page.\n\n`#ifexist:` does not work with interwiki links.\n\nA page that does not exist and is tested for using #ifexist will end up on the [Wanted Pages](https://www.mediawiki.org/wiki/Manual:WantedPages)."
962
968
  },
963
969
  {
964
970
  "aliases": [
@@ -2255,7 +2261,7 @@
2255
2261
  }
2256
2262
  ]
2257
2263
  ],
2258
- "description": "Number of active users, based on the criteria used in Special:Statistics."
2264
+ "description": "Number of active users, based on the criteria used in [Special:Statistics](https://www.mediawiki.org/wiki/Special:Statistics)."
2259
2265
  },
2260
2266
  {
2261
2267
  "aliases": [
package/dist/base.d.mts CHANGED
@@ -128,11 +128,10 @@ export interface LanguageService {
128
128
  * Provide color decorators
129
129
  *
130
130
  * 提供颜色指示
131
- * @param rgba color parser / 颜色解析函数
132
131
  * @param text source Wikitext / 源代码
133
- * @param hsl whether HSL colors are treated / 是否允许HSL颜色
132
+ * @param rgba color parser / 颜色解析函数
134
133
  */
135
- provideDocumentColors(rgba: (s: string) => [number, number, number, number] | [], text: string, hsl?: boolean): Promise<ColorInformation[]>;
134
+ provideDocumentColors(text: string, rgba?: (s: string) => [number, number, number, number] | []): Promise<ColorInformation[]>;
136
135
  /**
137
136
  * Provide color pickers
138
137
  *
package/dist/base.d.ts CHANGED
@@ -128,11 +128,10 @@ export interface LanguageService {
128
128
  * Provide color decorators
129
129
  *
130
130
  * 提供颜色指示
131
- * @param rgba color parser / 颜色解析函数
132
131
  * @param text source Wikitext / 源代码
133
- * @param hsl whether HSL colors are treated / 是否允许HSL颜色
132
+ * @param rgba color parser / 颜色解析函数
134
133
  */
135
- provideDocumentColors(rgba: (s: string) => [number, number, number, number] | [], text: string, hsl?: boolean): Promise<ColorInformation[]>;
134
+ provideDocumentColors(text: string, rgba?: (s: string) => [number, number, number, number] | []): Promise<ColorInformation[]>;
136
135
  /**
137
136
  * Provide color pickers
138
137
  *
package/dist/bin/cli.js CHANGED
@@ -11,6 +11,7 @@ const util_1 = __importDefault(require("util"));
11
11
  const index_1 = __importDefault(require("../index"));
12
12
  const man = `
13
13
  Available options:
14
+ -- End of options
14
15
  -c, --config <path or preset config> Choose parser's configuration
15
16
  --cache Enable caching
16
17
  --cache-file <path> Specify cache file
@@ -228,6 +229,10 @@ for (let i = 2; i < argv.length; i++) {
228
229
  process.exit(0);
229
230
  break;
230
231
  }
232
+ case '--':
233
+ files = [...files, ...argv.slice(i + 1)];
234
+ i = argv.length;
235
+ break;
231
236
  default: {
232
237
  let known = true;
233
238
  if (option.includes('=')) {
@@ -277,6 +282,9 @@ if (quiet && strict) {
277
282
  }
278
283
  if (recursive) {
279
284
  files = files.flatMap(file => {
285
+ if (!fs_1.default.existsSync(file)) {
286
+ return [];
287
+ }
280
288
  if (fs_1.default.statSync(file).isFile()) {
281
289
  return file;
282
290
  }
@@ -347,6 +355,9 @@ const { mtimeMs } = fs_1.default.statSync(config), obj = cache?.[include ? 'incl
347
355
  process.exit(0);
348
356
  }
349
357
  for (const file of new Set(files.map(f => path_1.default.resolve(f)))) {
358
+ if (!fs_1.default.existsSync(file)) {
359
+ continue;
360
+ }
350
361
  const stat = fs_1.default.statSync(file);
351
362
  if (stat.isDirectory()) {
352
363
  console.error(`"${file}" is a directory. Please use -r or --recursive to lint directories.`);
@@ -41,7 +41,7 @@ const filterGadget = (id) => {
41
41
  const n = Number(id);
42
42
  return n < 2300 || n > 2303; // Gadget, Gadget talk, Gadget definition, Gadget definition talk
43
43
  };
44
- const pkg = 'wikilint', version = '2.46.2';
44
+ const pkg = 'wikilint', version = '2.46.4';
45
45
  /**
46
46
  * Get the parser configuration for a Wikimedia Foundation project.
47
47
  * @param site site nickname
@@ -1,5 +1,5 @@
1
1
  import type { Position } from 'vscode-languageserver-types';
2
- import type { TextDocument } from 'vscode-languageserver-textdocument';
2
+ import type { TextDocument, Range } from 'vscode-languageserver-textdocument';
3
3
  import type { JSONDocument, LanguageService as JSONLanguageService } from 'vscode-json-languageservice';
4
4
  import type { Stylesheet, LanguageService as CSSLanguageService } from 'vscode-css-languageservice';
5
5
  import type { IHTMLDataProvider } from 'vscode-html-languageservice';
@@ -37,13 +37,15 @@ export declare const loadCssLSP: () => CSSLanguageService | null;
37
37
  export declare const loadHtmlData: () => IHTMLDataProvider | null;
38
38
  export declare const loadStylelint: () => PublicApi | null;
39
39
  /** embedded document */
40
- declare class EmbeddedDocument implements TextDocument {
40
+ declare abstract class EmbeddedDocument implements TextDocument {
41
41
  #private;
42
42
  languageId: string;
43
43
  lineCount: number;
44
44
  pre: string;
45
45
  uri: string;
46
46
  version: number;
47
+ abstract getLineRange(): Range;
48
+ abstract getEOLCharacters(): string;
47
49
  /**
48
50
  * @param id language ID
49
51
  * @param root root token
@@ -64,6 +66,8 @@ declare class EmbeddedDocument implements TextDocument {
64
66
  /** embedded JSON document */
65
67
  export declare class EmbeddedJSONDocument extends EmbeddedDocument {
66
68
  jsonDoc: JSONDocument;
69
+ abstract getLineRange(): Range;
70
+ abstract getEOLCharacters(): string;
67
71
  /**
68
72
  * @param root root token
69
73
  * @param token current token
@@ -73,6 +77,8 @@ export declare class EmbeddedJSONDocument extends EmbeddedDocument {
73
77
  /** embedded CSS document */
74
78
  export declare class EmbeddedCSSDocument extends EmbeddedDocument {
75
79
  styleSheet: Stylesheet;
80
+ abstract getLineRange(): Range;
81
+ abstract getEOLCharacters(): string;
76
82
  /**
77
83
  * @param root root token
78
84
  * @param token current token
package/dist/lib/lsp.d.ts CHANGED
@@ -22,10 +22,10 @@ export declare class LanguageService implements LanguageServiceBase {
22
22
  * Provide color decorators
23
23
  *
24
24
  * 提供颜色指示
25
- * @param rgba color parser / 颜色解析函数
26
25
  * @param text source Wikitext / 源代码
26
+ * @param rgba color parser / 颜色解析函数
27
27
  */
28
- provideDocumentColors(rgba: (s: string) => [number, number, number, number] | [], text: string): Promise<ColorInformation[]>;
28
+ provideDocumentColors(text: string, rgba?: (color: string) => [number, number, number, number] | []): Promise<ColorInformation[]>;
29
29
  /**
30
30
  * Provide color pickers
31
31
  *
package/dist/lib/lsp.js CHANGED
@@ -498,10 +498,13 @@ class LanguageService {
498
498
  * Provide color decorators
499
499
  *
500
500
  * 提供颜色指示
501
- * @param rgba color parser / 颜色解析函数
502
501
  * @param text source Wikitext / 源代码
502
+ * @param rgba color parser / 颜色解析函数
503
503
  */
504
- async provideDocumentColors(rgba, text) {
504
+ async provideDocumentColors(text, rgba = color_1.rgba) {
505
+ if (typeof text === 'function' && typeof rgba === 'string') {
506
+ [rgba, text] = [text, rgba];
507
+ }
505
508
  const root = await this.#queue(text);
506
509
  return root.querySelectorAll('attr-value,parameter-value,arg-default').reverse().flatMap(token => {
507
510
  const { type, childNodes,
@@ -608,7 +611,7 @@ class LanguageService {
608
611
  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;
609
612
  if (mt?.[1] !== undefined) { // tag
610
613
  const closing = mt[1].startsWith('/');
611
- return getCompletion(allTags, 'Class', mt[1].slice(closing ? 1 : 0), position, closing && !curLine?.slice(character).trim().startsWith('>') ? '>' : '');
614
+ return getCompletion(allTags, 'Class', mt[1].slice(closing ? 1 : 0), position, closing && !curLine?.slice(character).trimStart().startsWith('>') ? '>' : '');
612
615
  }
613
616
  else if (mt?.[4] || mt?.[5] && jaSwitches.length > 0) { // behavior switch
614
617
  const isJa = mt[5] !== undefined;
package/dist/lib/title.js CHANGED
@@ -8,7 +8,7 @@ const string_1 = require("../util/string");
8
8
  * @param title 标题
9
9
  */
10
10
  const resolve = (title) => {
11
- const [, { length }, sub] = /^((?:\.\.\/)*)([\s\S]*)/u.exec(title);
11
+ const [, { length }, sub] = /^((?:\.\.\/)*)(.*)/su.exec(title);
12
12
  return [length / 3, sub];
13
13
  };
14
14
  /**
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.attributesParent = void 0;
4
+ /* NOT FOR BROWSER ONLY */
4
5
  const debug_1 = require("../util/debug");
5
6
  /**
6
7
  * 子节点含有AttributesToken的类
@@ -20,7 +21,9 @@ const attributesParent = (i = 0) => (constructor) => {
20
21
  return this.#getAttributesChild().getAttr(key);
21
22
  }
22
23
  }
24
+ /* NOT FOR BROWSER ONLY */
23
25
  (0, debug_1.mixin)(AttributesParent, constructor);
26
+ /* NOT FOR BROWSER ONLY END */
24
27
  return AttributesParent;
25
28
  }
26
29
  };
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.elementLike = void 0;
4
- const debug_1 = require("../util/debug");
5
4
  const selector_1 = require("../util/selector");
5
+ /* NOT FOR BROWSER ONLY */
6
+ const debug_1 = require("../util/debug");
6
7
  /** @ignore */
7
8
  const elementLike = (constructor) => {
8
9
  LINT: {
@@ -67,7 +68,9 @@ const elementLike = (constructor) => {
67
68
  }
68
69
  }
69
70
  }
71
+ /* NOT FOR BROWSER ONLY */
70
72
  (0, debug_1.mixin)(ElementLike, constructor);
73
+ /* NOT FOR BROWSER ONLY END */
71
74
  return ElementLike;
72
75
  }
73
76
  };
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ /* NOT FOR BROWSER ONLY */
2
3
  Object.defineProperty(exports, "__esModule", { value: true });
3
4
  exports.gapped = void 0;
4
5
  const debug_1 = require("../util/debug");
6
+ /* NOT FOR BROWSER ONLY END */
5
7
  /**
6
8
  * 给定 gap 的类
7
9
  * @param gap
@@ -12,7 +14,9 @@ const gapped = (gap = 1) => (constructor) => {
12
14
  return gap;
13
15
  }
14
16
  }
17
+ /* NOT FOR BROWSER ONLY */
15
18
  (0, debug_1.mixin)(GappedToken, constructor);
19
+ /* NOT FOR BROWSER ONLY END */
16
20
  return GappedToken;
17
21
  };
18
22
  exports.gapped = gapped;
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.hiddenToken = void 0;
4
+ /* NOT FOR BROWSER ONLY */
4
5
  const debug_1 = require("../util/debug");
6
+ /* NOT FOR BROWSER ONLY END */
5
7
  /**
6
8
  * 解析后不可见的类
7
9
  * @param linter 是否覆写 lint 方法
@@ -16,7 +18,9 @@ const hiddenToken = (linter = true, html = true) => (constructor) => {
16
18
  LINT: return linter ? [] : super.lint(start);
17
19
  }
18
20
  }
21
+ /* NOT FOR BROWSER ONLY */
19
22
  (0, debug_1.mixin)(AnyHiddenToken, constructor);
23
+ /* NOT FOR BROWSER ONLY END */
20
24
  return AnyHiddenToken;
21
25
  };
22
26
  exports.hiddenToken = hiddenToken;
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ /* NOT FOR BROWSER ONLY */
2
3
  Object.defineProperty(exports, "__esModule", { value: true });
3
4
  exports.noEscape = void 0;
4
5
  const debug_1 = require("../util/debug");
6
+ /* NOT FOR BROWSER ONLY END */
5
7
  /**
6
8
  * 不需要转义的类
7
9
  * @ignore
@@ -13,7 +15,9 @@ const noEscape = (constructor) => {
13
15
  //
14
16
  }
15
17
  }
18
+ /* NOT FOR BROWSER ONLY */
16
19
  (0, debug_1.mixin)(NoEscapeToken, constructor);
20
+ /* NOT FOR BROWSER ONLY END */
17
21
  return NoEscapeToken;
18
22
  }
19
23
  };
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.nodeLike = void 0;
4
+ /* NOT FOR BROWSER ONLY */
4
5
  const debug_1 = require("../util/debug");
5
6
  /** @ignore */
6
7
  const nodeLike = (constructor) => {
@@ -18,7 +19,9 @@ const nodeLike = (constructor) => {
18
19
  LINT: return this.getDimension().width;
19
20
  }
20
21
  }
22
+ /* NOT FOR BROWSER ONLY */
21
23
  (0, debug_1.mixin)(NodeLike, constructor);
24
+ /* NOT FOR BROWSER ONLY END */
22
25
  return NodeLike;
23
26
  };
24
27
  exports.nodeLike = nodeLike;
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ /* NOT FOR BROWSER ONLY */
2
3
  Object.defineProperty(exports, "__esModule", { value: true });
3
4
  exports.padded = void 0;
4
5
  const debug_1 = require("../util/debug");
6
+ /* NOT FOR BROWSER ONLY END */
5
7
  /**
6
8
  * 给定 padding 的类
7
9
  * @param padding padding 字符串
@@ -13,7 +15,9 @@ const padded = ({ length }) => (constructor) => {
13
15
  return key === 'padding' ? length : super.getAttribute(key);
14
16
  }
15
17
  }
18
+ /* NOT FOR BROWSER ONLY */
16
19
  (0, debug_1.mixin)(PaddedToken, constructor);
20
+ /* NOT FOR BROWSER ONLY END */
17
21
  return PaddedToken;
18
22
  };
19
23
  exports.padded = padded;
@@ -4,10 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.paramLineParent = void 0;
7
- const debug_1 = require("../util/debug");
8
7
  const lint_1 = require("../util/lint");
9
8
  const rect_1 = require("../lib/rect");
10
9
  const index_1 = __importDefault(require("../index"));
10
+ /* NOT FOR BROWSER ONLY */
11
+ const debug_1 = require("../util/debug");
11
12
  /**
12
13
  * 子节点为ParamLineToken的类
13
14
  * @ignore
@@ -44,7 +45,9 @@ const paramLineParent = (constructor) => {
44
45
  return errors;
45
46
  }
46
47
  }
48
+ /* NOT FOR BROWSER ONLY */
47
49
  (0, debug_1.mixin)(ParamLineParent, constructor);
50
+ /* NOT FOR BROWSER ONLY END */
48
51
  return ParamLineParent;
49
52
  }
50
53
  };
@@ -10,8 +10,8 @@ const ext_1 = require("../src/tagPair/ext");
10
10
  const comment_1 = require("../src/nowiki/comment");
11
11
  const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', { length } = onlyincludeLeft, getExtRegex = [false, true].map(includeOnly => {
12
12
  const noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly';
13
- return (0, common_1.getRegex)(exts => new RegExp(String.raw `<!--[\s\S]*?(?:-->|$)|<${noincludeRegex}(?:\s[^>]*)?/?>|</${noincludeRegex}\s*>|<(${exts // eslint-disable-next-line unicorn/prefer-string-raw
14
- })(\s[^>]*?)?(?:/>|>([\s\S]*?)</(${'\\1'}\s*)>)|<(${includeRegex})(\s[^>]*?)?(?:/>|>([\s\S]*?)(?:</(${includeRegex}\s*)>|$))`, 'giu'));
13
+ return (0, common_1.getRegex)(exts => new RegExp(String.raw `<!--.*?(?:-->|$)|<${noincludeRegex}(?:\s[^>]*)?/?>|</${noincludeRegex}\s*>|<(${exts // eslint-disable-next-line unicorn/prefer-string-raw
14
+ })(\s[^>]*?)?(?:/>|>(.*?)</(${'\\1'}\s*)>)|<(${includeRegex})(\s[^>]*?)?(?:/>|>(.*?)(?:</(${includeRegex}\s*)>|$))`, 'gisu'));
15
15
  });
16
16
  /**
17
17
  * 更新`<onlyinclude>`和`</onlyinclude>`的位置
@@ -65,10 +65,10 @@ const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
65
65
  const stack = [];
66
66
  newExt = ext.filter(e => e !== 'translate' && e !== 'tvar');
67
67
  newConfig = { ...config, ext: newExt };
68
- wikitext = wikitext.replace(/<nowiki>[\s\S]*?<\/nowiki>/giu, m => {
68
+ wikitext = wikitext.replace(/<nowiki>.*?<\/nowiki>/gisu, m => {
69
69
  stack.push(m);
70
70
  return `\0${stack.length - 1}\x7F`;
71
- }).replace(/<translate( nowrap)?>([\s\S]*?)<\/translate>/gu, (_, p1, p2) => {
71
+ }).replace(/<translate( nowrap)?>(.*?)<\/translate>/gsu, (_, p1, p2) => {
72
72
  const l = accum.length;
73
73
  // @ts-expect-error abstract class
74
74
  new TranslateToken(p1, p2 && (0, string_1.restore)(p2, stack), newConfig, accum);
@@ -11,7 +11,7 @@ const externalLinks_1 = require("./externalLinks");
11
11
  const index_2 = require("../src/link/index");
12
12
  const file_1 = require("../src/link/file");
13
13
  const category_1 = require("../src/link/category");
14
- const regexImg = /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(\||\0\d+!\x7F)([\s\S]*)$/u;
14
+ const regexImg = /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(\||\0\d+!\x7F)(.*)$/su;
15
15
  /**
16
16
  * 解析内部链接
17
17
  * @param wikitext
@@ -23,8 +23,8 @@ const regexImg = /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(\||\0\d+!\x7F)([\s\S]*)$/u;
23
23
  const parseLinks = (wikitext, config, accum, page, tidy) => {
24
24
  config.regexLinks ??= new RegExp(String.raw `^\s*(?:${config.protocol}|//)`, 'iu');
25
25
  const regex = config.inExt
26
- ? /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(?:(\||\0\d+!\x7F)([\s\S]*?[^\]]))?\]\]([\s\S]*)$/u
27
- : /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])*)(?:(\||\0\d+!\x7F)([\s\S]*?[^\]])?)?\]\]([\s\S]*)$/u, bits = wikitext.split('[[');
26
+ ? /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(?:(\||\0\d+!\x7F)(.*?[^\]]))?\]\](.*)$/su
27
+ : /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])*)(?:(\||\0\d+!\x7F)(.*?[^\]])?)?\]\](.*)$/su, bits = wikitext.split('[[');
28
28
  let s = bits.shift();
29
29
  for (let i = 0; i < bits.length; i++) {
30
30
  let mightBeImg = false, link, delimiter, text, after;
@@ -62,7 +62,7 @@ class AttributesToken extends index_2.Token {
62
62
  this.#type = type;
63
63
  this.setAttribute('name', name);
64
64
  if (attr) {
65
- const regex = /([^\s/](?:(?!\0\d+~\x7F)[^\s/=])*)(?:((?:\s(?:\s|\0\d+[cn]\x7F)*)?(?:=|\0\d+~\x7F)(?:\s|\0\d+[cn]\x7F)*)(?:(["'])([\s\S]*?)(\3|$)|(\S*)))?/gu;
65
+ const regex = /([^\s/](?:(?!\0\d+~\x7F)[^\s/=])*)(?:((?:\s(?:\s|\0\d+[cn]\x7F)*)?(?:=|\0\d+~\x7F)(?:\s|\0\d+[cn]\x7F)*)(?:(["'])(.*?)(\3|$)|(\S*)))?/gsu;
66
66
  let out = '', mt = regex.exec(attr), lastIndex = 0;
67
67
  const insertDirty = /** 插入无效属性 */ () => {
68
68
  if (out) {
@@ -131,18 +131,12 @@ let LinkBaseToken = (() => {
131
131
  }
132
132
  /** @private */
133
133
  text() {
134
- const { length, firstChild, childNodes, type, } = this, target = firstChild.text();
135
- let str;
136
- if (length === 1) {
137
- str =
138
- type === 'link' ?
139
- (0, string_1.trimStart)(target) :
140
- target.trim();
141
- }
142
- else {
143
- str = `${target.trim()}|${(0, string_1.text)(childNodes.slice(1)
134
+ const { length, firstChild, childNodes, type, } = this, target = firstChild.text(), str = length === 1
135
+ ? target[type === 'link' ?
136
+ 'trimStart' :
137
+ 'trim']()
138
+ : `${target.trim()}|${(0, string_1.text)(childNodes.slice(1)
144
139
  .filter(({ type: t, name }) => t !== 'image-parameter' || name !== 'invalid'), '|')}`;
145
- }
146
140
  if (this.#bracket) {
147
141
  return `[[${str}]]`;
148
142
  }
@@ -17,24 +17,26 @@ class GalleryToken extends index_2.MultiLineToken {
17
17
  /** @param inner 标签内部wikitext */
18
18
  constructor(inner, config, accum = []) {
19
19
  super(undefined, config, accum, {});
20
- for (const line of inner?.split('\n') ?? []) {
21
- const matches = /^([^|]+)(?:\|(.*))?/u.exec(line);
22
- if (!matches) {
23
- super.insertAt((line.trim()
20
+ if (inner) {
21
+ for (const line of inner.split('\n')) {
22
+ const matches = /^([^|]+)(?:\|(.*))?/u.exec(line);
23
+ if (!matches) {
24
+ super.insertAt((line.trim()
25
+ // @ts-expect-error abstract class
26
+ ? new commentLine_1.CommentLineToken(line, config, accum)
27
+ : line));
28
+ continue;
29
+ }
30
+ const [, file, alt] = matches;
31
+ if (this.#checkFile(file)) {
24
32
  // @ts-expect-error abstract class
25
- ? new commentLine_1.CommentLineToken(line, config, accum)
26
- : line));
27
- continue;
28
- }
29
- const [, file, alt] = matches;
30
- if (this.#checkFile(file)) {
31
- // @ts-expect-error abstract class
32
- super.insertAt(new galleryImage_1.GalleryImageToken('gallery', file, alt, config, accum));
33
- }
34
- else {
35
- super.insertAt(
36
- // @ts-expect-error abstract class
37
- new commentLine_1.CommentLineToken(line, config, accum));
33
+ super.insertAt(new galleryImage_1.GalleryImageToken('gallery', file, alt, config, accum));
34
+ }
35
+ else {
36
+ super.insertAt(
37
+ // @ts-expect-error abstract class
38
+ new commentLine_1.CommentLineToken(line, config, accum));
39
+ }
38
40
  }
39
41
  }
40
42
  }
@@ -64,7 +64,7 @@ class NestedToken extends index_2.Token {
64
64
  if (!s) {
65
65
  return errors;
66
66
  }
67
- const rect = new rect_1.BoundingRect(this, start), msg = index_1.default.msg('invalid-content', name), regex = typeof this.#regex === 'boolean' ? lintRegex[this.#regex ? 1 : 0] : /^<!--[\s\S]*-->$/u, ignored = childNodes.filter(child => {
67
+ const rect = new rect_1.BoundingRect(this, start), msg = index_1.default.msg('invalid-content', name), regex = typeof this.#regex === 'boolean' ? lintRegex[this.#regex ? 1 : 0] : /^<!--.*-->$/su, ignored = childNodes.filter(child => {
68
68
  const { type, name: n } = child;
69
69
  if (type === 'ext') {
70
70
  return !this.#tags.includes(n);
@@ -152,14 +152,14 @@ let ExtToken = (() => {
152
152
  // NestedToken 依赖 ExtToken
153
153
  const { NestedToken } = require('../nested');
154
154
  // @ts-expect-error abstract class
155
- innerToken = new NestedToken(inner, /<(option|choicetemplate)(\s[^>]*?)?(?:\/>|>([\s\S]*?)<\/(\1)>)/gu, ['option', 'choicetemplate'], newConfig, accum);
155
+ innerToken = new NestedToken(inner, /<(option|choicetemplate)(\s[^>]*?)?(?:\/>|>(.*?)<\/(\1)>)/gsu, ['option', 'choicetemplate'], newConfig, accum);
156
156
  break;
157
157
  }
158
158
  case 'combobox': {
159
159
  // NestedToken 依赖 ExtToken
160
160
  const { NestedToken } = require('../nested');
161
161
  // @ts-expect-error abstract class
162
- innerToken = new NestedToken(inner, /<(combooption)(\s[^>]*?)?(?:\/>|>([\s\S]*?)<\/(combooption\s*)>)/giu, ['combooption'], newConfig, accum);
162
+ innerToken = new NestedToken(inner, /<(combooption)(\s[^>]*?)?(?:\/>|>(.*?)<\/(combooption\s*)>)/gisu, ['combooption'], newConfig, accum);
163
163
  break;
164
164
  }
165
165
  case 'gallery': {
@@ -22,13 +22,13 @@ class TranslateToken extends index_2.TagPairToken {
22
22
  */
23
23
  constructor(attr, inner, config, accum = []) {
24
24
  const attrToken = new syntax_1.SyntaxToken(attr, 'translate-attr', config, accum);
25
- inner = inner?.replace(/<tvar(\|[^>]+)>([\s\S]*?)<\/>/gu, (_, p1, p2) => {
25
+ inner = inner?.replace(/<tvar(\|[^>]+)>(.*?)<\/>/gsu, (_, p1, p2) => {
26
26
  // @ts-expect-error abstract class
27
27
  new tvar_1.TvarToken('tvar', p1, false, config, accum);
28
28
  // @ts-expect-error abstract class
29
29
  new tvar_1.TvarToken('', '', true, config, accum);
30
30
  return `\0${accum.length - 2}n\x7F${p2}\0${accum.length}n\x7F`;
31
- }).replace(/<(tvar)(\s+name\s*=(?:\s*(?:(["'])[\s\S]*?\3|[^"'\s>]+))?\s*)>([\s\S]*?)<\/(tvar)(\s*)>/giu, (_, p1, p2, __, p3, p4, p5) => {
31
+ }).replace(/<(tvar)(\s+name\s*=(?:\s*(?:(["']).*?\3|[^"'\s>]+))?\s*)>(.*?)<\/(tvar)(\s*)>/gisu, (_, p1, p2, __, p3, p4, p5) => {
32
32
  // @ts-expect-error abstract class
33
33
  new tvar_1.TvarToken(p1, p2, false, config, accum);
34
34
  // @ts-expect-error abstract class
@@ -129,7 +129,7 @@ let TranscludeToken = (() => {
129
129
  }
130
130
  const colon = title.search(/[::]/u), fullWidth = title[colon] === ':', isFunction = colon !== -1;
131
131
  if (isFunction || parts.length === 0 && !this.#raw) {
132
- const magicWord = isFunction ? title.slice(0, colon) : title, arg = isFunction && title.slice(colon + 1), cleaned = (0, string_1.removeComment)(magicWord), name = isFunction ? (0, string_1.trimStart)(cleaned) + (fullWidth ? ':' : '') : cleaned.trim(),
132
+ const magicWord = isFunction ? title.slice(0, colon) : title, arg = isFunction && title.slice(colon + 1), cleaned = (0, string_1.removeComment)(magicWord), name = isFunction ? cleaned.trimStart() + (fullWidth ? ':' : '') : cleaned.trim(),
133
133
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
134
134
  [, isSensitive, canonicalName] = (0, debug_1.getMagicWordInfo)(name, parserFunction), isFunc = !('functionHook' in config) || functionHook.includes(canonicalName), isVar = variable.includes(canonicalName);
135
135
  if (isFunction ? canonicalName && isFunc : isVar) {
@@ -12,7 +12,7 @@ exports.enMsg = (() => {
12
12
  // eslint-disable-next-line n/no-missing-require
13
13
  '../../i18n/en.json');
14
14
  })();
15
- exports.galleryParams = new Set(['alt', 'link', 'lang', 'page', 'caption']);
15
+ exports.galleryParams = new Set(['alt', 'link', 'lang', 'page', 'caption', 'class']);
16
16
  exports.extensions = new Set(['tiff', 'tif', 'png', 'gif', 'jpg', 'jpeg', 'webp', 'xcf', 'pdf', 'svg', 'djvu']);
17
17
  exports.jsonTags = ['templatedata', 'mapframe', 'maplink'];
18
18
  /* NOT FOR BROWSER ONLY */
package/dist/util/lint.js CHANGED
@@ -16,7 +16,7 @@ const tableTags = new Set(['tr', 'td', 'th', 'caption']), tableTemplates = new S
16
16
  const isFostered = (token) => {
17
17
  const first = token.childNodes.find(child => child.text().trim());
18
18
  if (!first
19
- || first.type === 'text' && first.data.trim().startsWith('!')
19
+ || first.type === 'text' && first.data.trimStart().startsWith('!')
20
20
  || first.is('magic-word') && first.name === '!'
21
21
  || first.is('template') && tableTemplates.has(first.name)
22
22
  || first.is('html') && tableTags.has(first.name)) {
@@ -1,8 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toLowerCase = exports.decodeNumber = exports.decodeHtml = exports.decodeHtmlBasic = exports.text = exports.removeCommentLine = exports.removeComment = exports.tidy = exports.trimStart = exports.trimLc = exports.extUrlChar = exports.extUrlCharFirst = exports.zs = void 0;
3
+ exports.toLowerCase = exports.decodeNumber = exports.decodeHtml = exports.decodeHtmlBasic = exports.text = exports.removeCommentLine = exports.removeComment = exports.tidy = exports.trimLc = exports.extUrlChar = exports.extUrlCharFirst = exports.zs = void 0;
4
4
  exports.restore = restore;
5
- const common_1 = require("@bhsd/common");
6
5
  exports.zs = String.raw ` \xA0\u1680\u2000-\u200A\u202F\u205F\u3000`;
7
6
  const commonExtUrlChar = String.raw `[^[\]<>"\0-\x1F\x7F${exports.zs}\uFFFD]`;
8
7
  exports.extUrlCharFirst = String.raw `(?:\[[\da-f:.]+\]|${commonExtUrlChar})`;
@@ -13,12 +12,6 @@ exports.extUrlChar = String.raw `(?:${commonExtUrlChar}|\0\d+[cn!~]\x7F)*`;
13
12
  */
14
13
  const trimLc = (s) => s.trim().toLowerCase();
15
14
  exports.trimLc = trimLc;
16
- /**
17
- * trim starting whitespaces
18
- * @param s 字符串
19
- */
20
- const trimStart = (s) => s.slice((0, common_1.numLeadingSpaces)(s));
21
- exports.trimStart = trimStart;
22
15
  function restore(s, stack, translate) {
23
16
  if (translate === 1) {
24
17
  return s.replace(/\0(\d+)g\x7F/gu, (_, p1) => restore(String(stack[p1]), stack, 2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikilint",
3
- "version": "2.46.2",
3
+ "version": "2.46.4",
4
4
  "description": "A Node.js linter for MediaWiki markup",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -98,22 +98,22 @@
98
98
  }
99
99
  },
100
100
  "dependencies": {
101
- "@bhsd/cm-util": "^2.4.0",
102
- "@bhsd/common": "^4.0.1",
101
+ "@bhsd/cm-util": "^2.4.1",
102
+ "@bhsd/common": "^4.1.0",
103
103
  "@bhsd/stylelint-util": "^1.1.0",
104
104
  "culori": "^4.0.2",
105
105
  "entities": "^8.0.0",
106
106
  "vscode-languageserver-types": "^3.18.0"
107
107
  },
108
108
  "devDependencies": {
109
- "@bhsd/code-standard": "^4.0.0",
109
+ "@bhsd/code-standard": "^4.1.0",
110
110
  "@bhsd/nodejs": "^1.2.1",
111
- "@bhsd/test-util": "^3.1.0",
111
+ "@bhsd/test-util": "^3.1.2",
112
112
  "c8": "^12.0.0",
113
113
  "diff2html-cli": "^5.2.15",
114
- "esbuild": "^0.28.1",
115
- "eslint": "^10.8.0",
116
- "markdownlint-cli2": "^0.23.1",
114
+ "esbuild": "^0.28.2",
115
+ "eslint": "^10.9.1",
116
+ "markdownlint-cli2": "^0.23.2",
117
117
  "mathoid-texvcjs": "^0.6.0",
118
118
  "stylelint": "^17.14.1",
119
119
  "typescript": "^6.0.3",
@@ -121,7 +121,7 @@
121
121
  "vscode-css-languageservice": "^6.3.10",
122
122
  "vscode-html-languageservice": "^5.6.2",
123
123
  "vscode-json-languageservice": "^5.7.2",
124
- "vscode-languageserver-textdocument": "^1.0.12"
124
+ "vscode-languageserver-textdocument": "^1.0.13"
125
125
  },
126
126
  "engines": {
127
127
  "node": "^20.19.0 || ^22.13.0 || >=24.11.0"