wikilint 2.12.7 → 2.13.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.
@@ -704,6 +704,8 @@
704
704
  "PAGELANGUAGE",
705
705
  "=",
706
706
  "#FORMAL",
707
+ "#bcp47",
708
+ "#dir",
707
709
  "#timef",
708
710
  "#timefl"
709
711
  ],
@@ -740,7 +742,9 @@
740
742
  "静态重定向",
741
743
  "靜態重新導向",
742
744
  "NOGLOBAL",
743
- "EXPECTED_UNCONNECTED_PAGE"
745
+ "禁用全域用户页",
746
+ "EXPECTED_UNCONNECTED_PAGE",
747
+ "EXPECTUNUSEDTEMPLATE"
744
748
  ],
745
749
  {
746
750
  "forcetoc": "forcetoc",
@@ -768,7 +772,9 @@
768
772
  "目录": "toc",
769
773
  "目錄": "toc",
770
774
  "archivedtalk": "archivedtalk",
771
- "notalk": "notalk"
775
+ "已存档讨论": "archivedtalk",
776
+ "notalk": "notalk",
777
+ "禁用讨论": "notalk"
772
778
  }
773
779
  ],
774
780
  "protocol": "bitcoin:|ftp://|ftps://|geo:|git://|gopher://|http://|https://|irc://|ircs://|magnet:|mailto:|matrix:|mms://|news:|nntp://|redis://|sftp://|sip:|sips:|sms:|ssh://|svn://|tel:|telnet://|urn:|worldwind://|xmpp:",
@@ -120,6 +120,8 @@
120
120
  "101": "Portal talk",
121
121
  "118": "Draft",
122
122
  "119": "Draft talk",
123
+ "126": "MOS",
124
+ "127": "MOS talk",
123
125
  "710": "TimedText",
124
126
  "711": "TimedText talk",
125
127
  "828": "Module",
@@ -158,6 +160,8 @@
158
160
  "portal talk": 101,
159
161
  "draft": 118,
160
162
  "draft talk": 119,
163
+ "mos": 126,
164
+ "mos talk": 127,
161
165
  "timedtext": 710,
162
166
  "timedtext talk": 711,
163
167
  "module": 828,
@@ -333,6 +337,8 @@
333
337
  "PAGELANGUAGE",
334
338
  "=",
335
339
  "#FORMAL",
340
+ "#bcp47",
341
+ "#dir",
336
342
  "#timef",
337
343
  "#timefl"
338
344
  ],
@@ -357,7 +363,8 @@
357
363
  "NONEWSECTIONLINK",
358
364
  "STATICREDIRECT",
359
365
  "NOGLOBAL",
360
- "EXPECTED_UNCONNECTED_PAGE"
366
+ "EXPECTED_UNCONNECTED_PAGE",
367
+ "EXPECTUNUSEDTEMPLATE"
361
368
  ],
362
369
  {
363
370
  "forcetoc": "forcetoc",
@@ -122,6 +122,8 @@
122
122
  "103": "WikiProject talk",
123
123
  "118": "Draft",
124
124
  "119": "Draft talk",
125
+ "126": "MOS",
126
+ "127": "MOS talk",
125
127
  "710": "TimedText",
126
128
  "711": "TimedText talk",
127
129
  "828": "Module",
@@ -260,6 +262,8 @@
260
262
  "draft talk": 119,
261
263
  "草稿讨论": 119,
262
264
  "草稿討論": 119,
265
+ "mos": 126,
266
+ "mos talk": 127,
263
267
  "timedtext": 710,
264
268
  "timedtext talk": 711,
265
269
  "module": 828,
@@ -646,6 +650,8 @@
646
650
  "PAGELANGUAGE",
647
651
  "=",
648
652
  "#FORMAL",
653
+ "#bcp47",
654
+ "#dir",
649
655
  "#timef",
650
656
  "#timefl"
651
657
  ],
@@ -682,7 +688,9 @@
682
688
  "静态重定向",
683
689
  "靜態重新導向",
684
690
  "NOGLOBAL",
685
- "EXPECTED_UNCONNECTED_PAGE"
691
+ "禁用全域用户页",
692
+ "EXPECTED_UNCONNECTED_PAGE",
693
+ "EXPECTUNUSEDTEMPLATE"
686
694
  ],
687
695
  {
688
696
  "forcetoc": "forcetoc",
@@ -708,7 +716,11 @@
708
716
  "無目錄": "notoc",
709
717
  "toc": "toc",
710
718
  "目录": "toc",
711
- "目錄": "toc"
719
+ "目錄": "toc",
720
+ "archivedtalk": "archivedtalk",
721
+ "已存档讨论": "archivedtalk",
722
+ "notalk": "notalk",
723
+ "禁用讨论": "notalk"
712
724
  }
713
725
  ],
714
726
  "protocol": "bitcoin:|ftp://|ftps://|geo:|git://|gopher://|http://|https://|irc://|ircs://|magnet:|mailto:|matrix:|mms://|news:|nntp://|redis://|sftp://|sip:|sips:|sms:|ssh://|svn://|tel:|telnet://|urn:|worldwind://|xmpp:",
package/dist/base.d.ts CHANGED
@@ -12,6 +12,33 @@ export interface Config {
12
12
  readonly excludes?: string[];
13
13
  }
14
14
  export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | 'onlyinclude' | 'noinclude' | 'include' | 'comment' | 'ext' | 'ext-attrs' | 'ext-attr-dirty' | 'ext-attr' | 'attr-key' | 'attr-value' | 'ext-inner' | 'arg' | 'arg-name' | 'arg-default' | 'hidden' | 'magic-word' | 'magic-word-name' | 'invoke-function' | 'invoke-module' | 'template' | 'template-name' | 'parameter' | 'parameter-key' | 'parameter-value' | 'heading' | 'heading-title' | 'heading-trail' | 'html' | 'html-attrs' | 'html-attr-dirty' | 'html-attr' | 'table' | 'tr' | 'td' | 'table-syntax' | 'table-attrs' | 'table-attr-dirty' | 'table-attr' | 'table-inter' | 'td-inner' | 'hr' | 'double-underscore' | 'link' | 'link-target' | 'link-text' | 'category' | 'file' | 'gallery-image' | 'imagemap-image' | 'image-parameter' | 'quote' | 'ext-link' | 'ext-link-text' | 'ext-link-url' | 'free-ext-link' | 'magic-link' | 'list' | 'dd' | 'list-range' | 'converter' | 'converter-flags' | 'converter-flag' | 'converter-rule' | 'converter-rule-variant' | 'converter-rule-to' | 'converter-rule-from' | 'param-line' | 'imagemap-link';
15
+ export declare const stages: {
16
+ redirect: number;
17
+ onlyinclude: number;
18
+ noinclude: number;
19
+ include: number;
20
+ comment: number;
21
+ ext: number;
22
+ arg: number;
23
+ 'magic-word': number;
24
+ template: number;
25
+ heading: number;
26
+ html: number;
27
+ table: number;
28
+ hr: number;
29
+ 'double-underscore': number;
30
+ link: number;
31
+ category: number;
32
+ file: number;
33
+ quote: number;
34
+ 'ext-link': number;
35
+ 'free-ext-link': number;
36
+ 'magic-link': number;
37
+ list: number;
38
+ dd: number;
39
+ converter: number;
40
+ };
41
+ export type Stage = keyof typeof stages;
15
42
  export declare const rules: readonly ["bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "lonely-apos", "lonely-bracket", "lonely-http", "nested-link", "no-arg", "no-duplicate", "no-ignored", "obsolete-attr", "obsolete-tag", "parsing-order", "pipe-like", "table-layout", "tag-like", "unbalanced-header", "unclosed-comment", "unclosed-quote", "unclosed-table", "unescaped", "unknown-page", "unmatched-tag", "unterminated-url", "url-encoding", "var-anchor", "void-ext"];
16
43
  export declare namespace LintError {
17
44
  type Severity = 'error' | 'warning';
@@ -63,6 +90,6 @@ export interface Parser {
63
90
  * @param include 是否嵌入
64
91
  * @param maxStage 最大解析层级
65
92
  */
66
- parse(wikitext: string, include?: boolean, maxStage?: number, config?: Config): Token;
93
+ parse(wikitext: string, include?: boolean, maxStage?: number | Stage | Stage[], config?: Config): Token;
67
94
  }
68
95
  export {};
package/dist/base.js CHANGED
@@ -1,6 +1,33 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.rules = void 0;
3
+ exports.rules = exports.stages = void 0;
4
+ exports.stages = {
5
+ redirect: 1,
6
+ onlyinclude: 1,
7
+ noinclude: 1,
8
+ include: 1,
9
+ comment: 1,
10
+ ext: 1,
11
+ arg: 2,
12
+ 'magic-word': 2,
13
+ template: 2,
14
+ heading: 2,
15
+ html: 3,
16
+ table: 4,
17
+ hr: 5,
18
+ 'double-underscore': 5,
19
+ link: 6,
20
+ category: 6,
21
+ file: 6,
22
+ quote: 7,
23
+ 'ext-link': 8,
24
+ 'free-ext-link': 9,
25
+ 'magic-link': 9,
26
+ list: 10,
27
+ dd: 10,
28
+ converter: 11,
29
+ };
30
+ Object.setPrototypeOf(exports.stages, null);
4
31
  exports.rules = [
5
32
  'bold-header',
6
33
  'format-leakage',
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Config, LintError, TokenTypes, Parser as ParserBase } from './base';
1
+ import type { Config, LintError, TokenTypes, Parser as ParserBase, Stage } from './base';
2
2
  import type { Title } from './lib/title';
3
3
  import type { Token } from './internal';
4
4
  declare interface Parser extends ParserBase {
@@ -10,7 +10,7 @@ declare interface Parser extends ParserBase {
10
10
  * @param include 是否嵌入
11
11
  */
12
12
  normalizeTitle(title: string, defaultNs?: number, include?: boolean, config?: Config): Title;
13
- parse(wikitext: string, include?: boolean, maxStage?: number, config?: Config): Token;
13
+ parse(wikitext: string, include?: boolean, maxStage?: number | Stage | Stage[], config?: Config): Token;
14
14
  }
15
15
  declare const Parser: Parser;
16
16
  // @ts-expect-error mixed export styles
package/dist/index.js CHANGED
@@ -22,13 +22,13 @@ const Parser = {
22
22
  getConfig() {
23
23
  if (typeof this.config === 'string') {
24
24
  this.config = rootRequire(this.config, 'config');
25
+ if (this.config.doubleUnderscore.length < 3) {
26
+ (0, diff_1.error)(`The schema (${path.resolve(__dirname, '..', 'config', '.schema.json')}) of parser configuration is updated.`);
27
+ }
25
28
  return this.getConfig();
26
29
  }
27
30
  const { doubleUnderscore } = this.config;
28
- if (doubleUnderscore.length === 2) {
29
- (0, diff_1.error)(`The schema (${path.resolve(__dirname, '..', 'config', '.schema.json')}) of parser configuration is updated.`);
30
- }
31
- else if (doubleUnderscore[0].length === 0) {
31
+ if (doubleUnderscore.length > 2 && doubleUnderscore[0].length === 0) {
32
32
  doubleUnderscore[0] = Object.keys(doubleUnderscore[2]);
33
33
  }
34
34
  return {
@@ -78,6 +78,10 @@ const Parser = {
78
78
  /** @implements */
79
79
  parse(wikitext, include, maxStage = constants_1.MAX_STAGE, config = Parser.getConfig()) {
80
80
  wikitext = (0, string_1.tidy)(wikitext);
81
+ if (typeof maxStage !== 'number') {
82
+ const types = Array.isArray(maxStage) ? maxStage : [maxStage];
83
+ maxStage = Math.max(...types.map(t => base_1.stages[t] || constants_1.MAX_STAGE));
84
+ }
81
85
  const { Token } = require('./src/index');
82
86
  const root = debug_1.Shadow.run(() => {
83
87
  const token = new Token(wikitext, config);
@@ -3,15 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AstElement = void 0;
4
4
  const string_1 = require("../util/string");
5
5
  const debug_1 = require("../util/debug");
6
+ const selector_1 = require("../parser/selector");
6
7
  const node_1 = require("./node");
7
- /**
8
- * 将选择器转化为类型谓词
9
- * @param selector 选择器
10
- */
11
- const getCondition = (selector) => (({ type, name }) => selector.split(',').some(str => {
12
- const [t, ...ns] = str.trim().split('#');
13
- return (!t || t === type) && ns.every(n => n === name);
14
- }));
15
8
  /** 类似HTMLElement */
16
9
  class AstElement extends node_1.AstNode {
17
10
  /** 子节点总数 */
@@ -64,7 +57,7 @@ class AstElement extends node_1.AstNode {
64
57
  * @param selector 选择器
65
58
  */
66
59
  closest(selector) {
67
- const condition = getCondition(selector);
60
+ const condition = (0, selector_1.getCondition)(selector, this);
68
61
  let { parentNode } = this;
69
62
  while (parentNode) {
70
63
  if (condition(parentNode)) {
@@ -98,7 +91,7 @@ class AstElement extends node_1.AstNode {
98
91
  * @param selector 选择器
99
92
  */
100
93
  querySelector(selector) {
101
- const condition = getCondition(selector);
94
+ const condition = (0, selector_1.getCondition)(selector, this);
102
95
  return this.#getElementBy(condition);
103
96
  }
104
97
  /**
@@ -123,7 +116,7 @@ class AstElement extends node_1.AstNode {
123
116
  * @param selector 选择器
124
117
  */
125
118
  querySelectorAll(selector) {
126
- const condition = getCondition(selector);
119
+ const condition = (0, selector_1.getCondition)(selector, this);
127
120
  return this.#getElementsBy(condition);
128
121
  }
129
122
  /**
package/dist/lib/title.js CHANGED
@@ -52,7 +52,6 @@ class Title {
52
52
  */
53
53
  constructor(title, defaultNs, config, decode, selfLink) {
54
54
  const subpage = title.trim().startsWith('../');
55
- title = (0, string_1.decodeHtml)(title);
56
55
  if (decode && title.includes('%')) {
57
56
  try {
58
57
  const encoded = /%(?!21|3[ce]|5[bd]|7[b-d])[\da-f]{2}/iu.test(title);
@@ -61,7 +60,7 @@ class Title {
61
60
  }
62
61
  catch { }
63
62
  }
64
- title = title.replace(/[_ ]+/gu, ' ').trim();
63
+ title = (0, string_1.decodeHtml)(title).replace(/[_ ]+/gu, ' ').trim();
65
64
  if (subpage) {
66
65
  this.#ns = 0;
67
66
  }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCondition = void 0;
4
+ /**
5
+ * 将选择器转化为类型谓词
6
+ * @param selector 选择器
7
+ * @param scope 作用对象
8
+ * @param has `:has()`伪选择器
9
+ */
10
+ const getCondition = (selector, scope, has) => (({ type, name }) => selector.split(',').some(str => {
11
+ const [t, ...ns] = str.trim().split('#');
12
+ return (!t || t === type) && ns.every(n => n === name);
13
+ }));
14
+ exports.getCondition = getCondition;
@@ -104,18 +104,7 @@ class AttributesToken extends index_2.Token {
104
104
  errors.push(e);
105
105
  }
106
106
  for (const attr of childNodes) {
107
- if (attr instanceof atom_1.AtomToken && attr.text().trim()) {
108
- const e = (0, lint_1.generateForChild)(attr, rect, 'no-ignored', 'containing invalid attribute');
109
- e.suggestions = [
110
- {
111
- desc: 'remove',
112
- range: [e.startIndex, e.endIndex],
113
- text: ' ',
114
- },
115
- ];
116
- errors.push(e);
117
- }
118
- else if (attr instanceof attribute_1.AttributeToken) {
107
+ if (attr instanceof attribute_1.AttributeToken) {
119
108
  const { name } = attr;
120
109
  if (attrs.has(name)) {
121
110
  duplicated.add(name);
@@ -125,6 +114,20 @@ class AttributesToken extends index_2.Token {
125
114
  attrs.set(name, [attr]);
126
115
  }
127
116
  }
117
+ else {
118
+ const str = attr.text().trim();
119
+ if (str) {
120
+ const e = (0, lint_1.generateForChild)(attr, rect, 'no-ignored', 'containing invalid attribute', /[\p{L}\d]/u.test(str) ? 'error' : 'warning');
121
+ e.suggestions = [
122
+ {
123
+ desc: 'remove',
124
+ range: [e.startIndex, e.endIndex],
125
+ text: ' ',
126
+ },
127
+ ];
128
+ errors.push(e);
129
+ }
130
+ }
128
131
  }
129
132
  if (duplicated.size > 0) {
130
133
  for (const key of duplicated) {
@@ -265,7 +265,7 @@ class TranscludeToken extends index_2.Token {
265
265
  getPossibleValues() {
266
266
  const { type, name, childNodes } = this;
267
267
  if (type === 'template') {
268
- throw new Error(`TranscludeToken.getPossibleValues method is only for specific magic words!`);
268
+ throw new Error('TranscludeToken.getPossibleValues method is only for specific magic words!');
269
269
  }
270
270
  let start;
271
271
  switch (name) {
@@ -279,7 +279,7 @@ class TranscludeToken extends index_2.Token {
279
279
  start = 3;
280
280
  break;
281
281
  default:
282
- throw new Error(`TranscludeToken.getPossibleValues method is only for specific magic words!`);
282
+ throw new Error('TranscludeToken.getPossibleValues method is only for specific magic words!');
283
283
  }
284
284
  const queue = childNodes.slice(start, start + 2).map(({ childNodes: [, value] }) => value);
285
285
  for (let i = 0; i < queue.length;) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikilint",
3
- "version": "2.12.7",
3
+ "version": "2.13.0",
4
4
  "description": "A Node.js linter for MediaWiki markup",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -51,11 +51,11 @@
51
51
  "chalk": "^4.1.2"
52
52
  },
53
53
  "devDependencies": {
54
- "@bhsd/common": "^0.0.0",
54
+ "@bhsd/common": "^0.1.1",
55
55
  "@types/node": "^20.11.6",
56
56
  "v8r": "^3.0.0"
57
57
  },
58
58
  "engines": {
59
- "node": "^20.9.0"
59
+ "node": ">=20.9.0"
60
60
  }
61
61
  }