wikilint 2.13.7 → 2.13.9

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.
@@ -134,6 +134,8 @@
134
134
  "103": "WikiProject talk",
135
135
  "118": "Draft",
136
136
  "119": "Draft talk",
137
+ "126": "MOS",
138
+ "127": "MOS talk",
137
139
  "274": "Widget",
138
140
  "275": "Widget talk",
139
141
  "710": "TimedText",
@@ -279,6 +281,42 @@
279
281
  "分類討論": 15,
280
282
  "category‐ノート": 15,
281
283
  "カテゴリ・トーク": 15,
284
+ "p": 100,
285
+ "portal": 100,
286
+ "主题": 100,
287
+ "主題": 100,
288
+ "portal talk": 101,
289
+ "主题对话": 101,
290
+ "主题讨论": 101,
291
+ "主題對話": 101,
292
+ "主題討論": 101,
293
+ "pj": 102,
294
+ "wpj": 102,
295
+ "wikiproject": 102,
296
+ "专题": 102,
297
+ "專題": 102,
298
+ "維基專題": 102,
299
+ "维基专题": 102,
300
+ "pjt": 103,
301
+ "wpjt": 103,
302
+ "wikiproject talk": 103,
303
+ "专题对话": 103,
304
+ "专题讨论": 103,
305
+ "專題對話": 103,
306
+ "專題討論": 103,
307
+ "維基專題對話": 103,
308
+ "維基專題討論": 103,
309
+ "维基专题对话": 103,
310
+ "维基专题讨论": 103,
311
+ "draft": 118,
312
+ "草稿": 118,
313
+ "draft talk": 119,
314
+ "草稿讨论": 119,
315
+ "草稿討論": 119,
316
+ "mos": 126,
317
+ "mos talk": 127,
318
+ "timedtext": 710,
319
+ "timedtext talk": 711,
282
320
  "widget": 274,
283
321
  "widget talk": 275,
284
322
  "module": 828,
@@ -296,7 +334,10 @@
296
334
  "模組討論": 829,
297
335
  "模组对话": 829,
298
336
  "模组讨论": 829,
299
- "モジュール・トーク": 829
337
+ "モジュール・トーク": 829,
338
+ "topic": 2600,
339
+ "話題": 2600,
340
+ "话题": 2600
300
341
  },
301
342
  "parserFunction": [
302
343
  {
@@ -698,6 +739,8 @@
698
739
  "#FORMAL",
699
740
  "#bcp47",
700
741
  "#dir",
742
+ "#interwikilink",
743
+ "#interlanguagelink",
701
744
  "#timef",
702
745
  "#timefl"
703
746
  ],
@@ -775,26 +818,31 @@
775
818
  "thumbnail": "thumbnail",
776
819
  "thumb": "thumbnail",
777
820
  "缩略图": "thumbnail",
821
+ "縮圖": "thumbnail",
778
822
  "thumbnail=$1": "manualthumb",
779
823
  "thumb=$1": "manualthumb",
780
824
  "缩略图=$1": "manualthumb",
825
+ "縮圖=$1": "manualthumb",
781
826
  "left": "left",
782
827
  "左": "left",
783
828
  "right": "right",
784
829
  "右": "right",
785
830
  "none": "none",
786
831
  "无": "none",
832
+ "無": "none",
787
833
  "$1px": "width",
788
834
  "$1像素": "width",
789
835
  "center": "center",
790
836
  "centre": "center",
791
837
  "居中": "center",
838
+ "置中": "center",
792
839
  "framed": "framed",
793
840
  "enframed": "framed",
794
841
  "frame": "framed",
795
842
  "有框": "framed",
796
843
  "frameless": "frameless",
797
844
  "无框": "frameless",
845
+ "無框": "frameless",
798
846
  "upright": "upright",
799
847
  "右上": "upright",
800
848
  "upright=$1": "manual-upright",
@@ -803,36 +851,49 @@
803
851
  "右上$1": "manual-upright",
804
852
  "border": "border",
805
853
  "边框": "border",
854
+ "邊框": "border",
806
855
  "alt=$1": "alt",
807
856
  "替代=$1": "alt",
808
857
  "替代文本=$1": "alt",
809
858
  "class=$1": "class",
810
859
  "类=$1": "class",
860
+ "類別=$1": "class",
811
861
  "link=$1": "link",
812
862
  "链接=$1": "link",
863
+ "連結=$1": "link",
813
864
  "baseline": "baseline",
814
865
  "基线": "baseline",
815
866
  "sub": "sub",
816
867
  "子": "sub",
868
+ "下標": "sub",
817
869
  "super": "super",
818
870
  "sup": "super",
819
871
  "超": "super",
872
+ "上標": "super",
820
873
  "top": "top",
821
874
  "顶部": "top",
875
+ "垂直置頂": "top",
822
876
  "text-top": "text-top",
823
877
  "文字顶部": "text-top",
878
+ "文字置頂": "text-top",
824
879
  "middle": "middle",
825
880
  "中间": "middle",
881
+ "垂直置中": "middle",
826
882
  "bottom": "bottom",
827
883
  "底部": "bottom",
884
+ "垂直置底": "bottom",
828
885
  "text-bottom": "text-bottom",
829
886
  "文字底部": "text-bottom",
887
+ "文字置底": "text-bottom",
830
888
  "lang=$1": "lang",
831
889
  "语言=$1": "lang",
890
+ "語言=$1": "lang",
832
891
  "page=$1": "page",
833
892
  "page $1": "page",
834
893
  "页数=$1": "page",
835
- "$1页": "page"
894
+ "$1页": "page",
895
+ "頁=$1": "page",
896
+ "$1頁": "page"
836
897
  },
837
898
  "redirection": [
838
899
  "#redirect",
@@ -331,6 +331,8 @@
331
331
  "#FORMAL",
332
332
  "#bcp47",
333
333
  "#dir",
334
+ "#interwikilink",
335
+ "#interlanguagelink",
334
336
  "#timef",
335
337
  "#timefl"
336
338
  ],
@@ -73,11 +73,15 @@
73
73
  "0": "",
74
74
  "6": "File",
75
75
  "10": "Template",
76
+ "14": "Category",
76
77
  "828": "Module"
77
78
  },
78
79
  "nsid": {
80
+ "": 0,
79
81
  "file": 6,
80
- "category": 14
82
+ "template": 10,
83
+ "category": 14,
84
+ "module": 828
81
85
  },
82
86
  "parserFunction": [
83
87
  {
@@ -113,14 +117,44 @@
113
117
  "#vardefine": "vardefine",
114
118
  "#vardefineecho": "vardefineecho",
115
119
  "#widget": "widget",
116
- "#regex": "regex",
117
120
  "#related": "related",
118
- "#cscore": "cscore"
121
+ "#cscore": "cscore",
122
+ "#regex": "regex",
123
+ "#regex_var": "regex_var",
124
+ "#regexquote": "regexquote",
125
+ "#regexall": "regexall",
126
+ "#len": "len",
127
+ "#pos": "pos",
128
+ "#rpos": "rpos",
129
+ "#sub": "sub",
130
+ "#count": "count",
131
+ "#replace": "replace",
132
+ "#explode": "explode",
133
+ "#tab": "tab",
134
+ "#seo": "seo",
135
+ "#babel": "babel",
136
+ "#commaseparatedlist": "commaseparatedlist",
137
+ "#coordinates": "coordinates",
138
+ "#lst": "lst",
139
+ "#lsth": "lsth",
140
+ "#lstx": "lstx",
141
+ "#assessment": "assessment",
142
+ "#mentor": "mentor",
143
+ "#property": "property",
144
+ "#target": "target",
145
+ "#section": "section",
146
+ "#section-x": "section-x",
147
+ "#section-h": "section-h",
148
+ "#statements": "statements"
119
149
  },
120
150
  [
121
151
  "!",
122
152
  "=",
123
153
  "#FORMAL",
154
+ "#bcp47",
155
+ "#dir",
156
+ "#interwikilink",
157
+ "#interlanguagelink",
124
158
  "#timef",
125
159
  "#timefl"
126
160
  ],
@@ -250,7 +250,6 @@
250
250
  "nse": "nse",
251
251
  "url编码": "urlencode",
252
252
  "urlencode": "urlencode",
253
- "#urlencode": "urlencode",
254
253
  "小写首字": "lcfirst",
255
254
  "lcfirst": "lcfirst",
256
255
  "大写首字": "ucfirst",
@@ -652,6 +652,8 @@
652
652
  "#FORMAL",
653
653
  "#bcp47",
654
654
  "#dir",
655
+ "#interwikilink",
656
+ "#interlanguagelink",
655
657
  "#timef",
656
658
  "#timefl"
657
659
  ],
package/dist/base.d.ts CHANGED
@@ -45,6 +45,7 @@ export declare namespace LintError {
45
45
  interface Fix {
46
46
  readonly range: [number, number];
47
47
  text: string;
48
+ desc: string;
48
49
  }
49
50
  }
50
51
  export interface LintError {
package/dist/lib/text.js CHANGED
@@ -4,7 +4,7 @@ exports.AstText = void 0;
4
4
  const string_1 = require("../util/string");
5
5
  const index_1 = require("../index");
6
6
  const node_1 = require("./node");
7
- const source = String.raw `<\s*(?:/\s*)?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*?\])|((?:^|\])[^[]*?)\]+`, errorSyntax = new RegExp(String.raw `${source}|https?[:/]/+`, 'giu'), errorSyntaxUrl = new RegExp(source, 'giu'), extImage = new RegExp(String.raw `^https?://${string_1.extUrlCharFirst}${string_1.extUrlChar}\.(?:gif|png|jpg|jpeg)$`, 'iu'), regexes = {
7
+ const sp = String.raw `[\p{Zs}\t]*`, source = String.raw `<\s*(?:/\s*)?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*?\])|((?:^|\])[^[]*?)\]+|(?:rfc|pmid)(?=[-::]?${sp}\d)|isbn(?=[-::]?${sp}(?:\d(?:${sp}|-)){6})`, errorSyntax = new RegExp(String.raw `${source}|https?[:/]/+`, 'giu'), errorSyntaxUrl = new RegExp(source, 'giu'), extImage = new RegExp(String.raw `^https?://${string_1.extUrlCharFirst}${string_1.extUrlChar}\.(?:gif|png|jpg|jpeg)$`, 'iu'), noLinkTypes = new Set(['attr-value', 'ext-link-text', 'link-text']), regexes = {
8
8
  '[': /[[\]]/u,
9
9
  '{': /[{}]/u,
10
10
  ']': /[[\]](?=[^[\]]*$)/u,
@@ -16,6 +16,9 @@ const source = String.raw `<\s*(?:/\s*)?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*?\])
16
16
  ']': 'lonely-bracket',
17
17
  '}': 'lonely-bracket',
18
18
  h: 'lonely-http',
19
+ r: 'lonely-http',
20
+ p: 'lonely-http',
21
+ i: 'lonely-http',
19
22
  }, disallowedTags = [
20
23
  'html',
21
24
  'head',
@@ -101,28 +104,26 @@ class AstText extends node_1.AstNode {
101
104
  const errors = [], nextType = nextSibling?.type, nextName = nextSibling?.name, previousType = previousSibling?.type, root = this.getRootNode(), { ext, html } = root.getAttribute('config'), { top, left } = root.posFromIndex(start), tags = new Set(['onlyinclude', 'noinclude', 'includeonly', ext, html, disallowedTags].flat(2));
102
105
  for (let mt = errorRegex.exec(data); mt; mt = errorRegex.exec(data)) {
103
106
  const [, tag, prefix] = mt;
104
- let { 0: error, index } = mt;
107
+ let { index } = mt, error = mt[0].toLowerCase();
105
108
  if (prefix && prefix !== ']') {
106
109
  const { length } = prefix;
107
110
  index += length;
108
111
  error = error.slice(length);
109
112
  }
110
- const { 0: char, length } = error;
113
+ const { 0: char, length } = error, magicLink = char === 'r' || char === 'p' || char === 'i';
111
114
  if (char === '<' && !tags.has(tag.toLowerCase())
112
- || char === '['
113
- && type === 'ext-link-text'
114
- && (/&(?:rbrack|#93|#x5[Dd];);/u.test(data.slice(index + 1))
115
- || nextSibling?.is('ext') && nextName === 'nowiki' && nextSibling.innerText?.includes(']'))) {
115
+ || char === '[' && type === 'ext-link-text' && (/&(?:rbrack|#93|#x5[Dd];);/u.test(data.slice(index + 1))
116
+ || nextSibling?.is('ext') && nextName === 'nowiki' && nextSibling.innerText?.includes(']'))
117
+ || char === 'h' && index === 0 && type === 'ext-link-text' && extImage.test(data)
118
+ || magicLink && (!parentNode.getAttribute('plain') || noLinkTypes.has(type))) {
116
119
  continue;
117
120
  }
118
121
  else if (char === ']' && (index || length > 1)) {
119
122
  errorRegex.lastIndex--;
120
123
  }
121
- else if (char === 'h' && index === 0 && type === 'ext-link-text' && extImage.test(data)) {
122
- continue;
123
- }
124
124
  const startIndex = start + index, endIndex = startIndex + length, rootStr = root.toString(), nextChar = rootStr[endIndex], previousChar = rootStr[startIndex - 1], severity = length > 1 && !(char === '<' && !/[\s/>]/u.test(nextChar ?? '')
125
- || isHtmlAttrVal && (char === '[' || char === ']'))
125
+ || isHtmlAttrVal && (char === '[' || char === ']')
126
+ || magicLink && type === 'parameter-value')
126
127
  || char === '{' && (nextChar === char || previousChar === '-')
127
128
  || char === '}' && (previousChar === char || nextChar === '-')
128
129
  || char === '[' && (nextChar === char
@@ -150,9 +151,12 @@ class AstText extends node_1.AstNode {
150
151
  }
151
152
  }
152
153
  }
154
+ if (magicLink) {
155
+ error = error.toUpperCase();
156
+ }
153
157
  const lines = data.slice(0, index).split('\n'), startLine = lines.length + top - 1, line = lines[lines.length - 1], startCol = lines.length === 1 ? left + line.length : line.length, e = {
154
158
  rule: ruleMap[char],
155
- message: index_1.default.msg('lonely "$1"', char === 'h' ? error : char),
159
+ message: index_1.default.msg('lonely "$1"', magicLink || char === 'h' ? error : char),
156
160
  severity,
157
161
  startIndex,
158
162
  endIndex,
@@ -193,7 +197,7 @@ class AstText extends node_1.AstNode {
193
197
  }
194
198
  else if (char === ']' && previousType === 'free-ext-link' && severity === 'error') {
195
199
  const i = start - previousSibling.toString().length;
196
- e.fix = { range: [i, i], text: '[' };
200
+ e.fix = { range: [i, i], text: '[', desc: 'left bracket' };
197
201
  }
198
202
  errors.push(e);
199
203
  }
@@ -10,7 +10,7 @@ const closes = {
10
10
  '{': String.raw `\}{2,}|\|`,
11
11
  '-': String.raw `\}-`,
12
12
  '[': String.raw `\]\]`,
13
- }, marks = new Map([['!', '!'], ['!!', '+'], ['(!', '{'], ['!)', '}'], ['!-', '-'], ['=', '~'], ['server', 'm']]), re = /\{\{\s*([^\s\0<>[\]{}|_#&%:.]+)\s*\}\}(?!\})/gu;
13
+ }, openBraces = String.raw `|\{{2,}`, marks = new Map([['!', '!'], ['!!', '+'], ['(!', '{'], ['!)', '}'], ['!-', '-'], ['=', '~'], ['server', 'm']]), re = /\{\{\s*([^\s\0<>[\]{}|_#&%:.]+)\s*\}\}(?!\})/gu;
14
14
  /**
15
15
  * 解析花括号
16
16
  * @param wikitext
@@ -19,7 +19,7 @@ const closes = {
19
19
  * @throws TranscludeToken.constructor()
20
20
  */
21
21
  const parseBraces = (wikitext, config, accum) => {
22
- const source = String.raw `${config.excludes?.includes('heading') ? '' : String.raw `^((?:\0\d+[cno]\x7F)*)={1,6}|`}\[\[|-\{(?!\{)`, openBraces = String.raw `|\{{2,}`, { parserFunction: [, , , subst] } = config, stack = [];
22
+ const source = String.raw `${config.excludes?.includes('heading') ? '' : String.raw `^((?:\0\d+[cno]\x7F)*)={1,6}|`}\[\[|-\{(?!\{)`, { parserFunction: [, , , subst] } = config, stack = [];
23
23
  wikitext = wikitext.replace(re, (m, p1) => {
24
24
  // @ts-expect-error abstract class
25
25
  new transclude_1.TranscludeToken(m.slice(2, -2), [], config, accum);
@@ -6,6 +6,15 @@ const noinclude_1 = require("../src/nowiki/noinclude");
6
6
  const include_1 = require("../src/tagPair/include");
7
7
  const ext_1 = require("../src/tagPair/ext");
8
8
  const comment_1 = require("../src/nowiki/comment");
9
+ const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', { length } = onlyincludeLeft;
10
+ /**
11
+ * 更新`<onlyinclude>`和`</onlyinclude>`的位置
12
+ * @param wikitext
13
+ */
14
+ const update = (wikitext) => {
15
+ const i = wikitext.indexOf(onlyincludeLeft);
16
+ return { i, j: wikitext.indexOf(onlyincludeRight, i + length) };
17
+ };
9
18
  /**
10
19
  * 解析HTML注释和扩展标签
11
20
  * @param wikitext
@@ -14,14 +23,8 @@ const comment_1 = require("../src/nowiki/comment");
14
23
  * @param includeOnly 是否嵌入
15
24
  */
16
25
  const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
17
- const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', { length } = onlyincludeLeft;
18
- /** 更新`<onlyinclude>`和`</onlyinclude>`的位置 */
19
- const update = () => {
20
- const i = wikitext.indexOf(onlyincludeLeft);
21
- return { i, j: wikitext.indexOf(onlyincludeRight, i + length) };
22
- };
23
26
  if (includeOnly) {
24
- let { i, j } = update();
27
+ let { i, j } = update(wikitext);
25
28
  if (i !== -1 && j !== -1) { // `<onlyinclude>`拥有最高优先级
26
29
  let str = '';
27
30
  /**
@@ -41,7 +44,7 @@ const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
41
44
  }
42
45
  str += token;
43
46
  wikitext = wikitext.slice(j + length + 1);
44
- ({ i, j } = update());
47
+ ({ i, j } = update(wikitext));
45
48
  }
46
49
  if (wikitext) {
47
50
  noinclude(wikitext);
@@ -49,7 +52,9 @@ const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
49
52
  return str;
50
53
  }
51
54
  }
52
- const ext = config.ext.join('|'), noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly', regex = new RegExp(String.raw `<!--.*?(?:-->|$)|<${noincludeRegex}(?:\s[^>]*)?/?>|</${noincludeRegex}\s*>|<(${ext})(\s[^>]*?)?(?:/>|>(.*?)</(\1\s*)>)|<(${includeRegex})(\s[^>]*?)?(?:/>|>(.*?)(?:</(${includeRegex}\s*)>|$))`, 'gisu');
55
+ const ext = config.ext.join('|'), noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly',
56
+ /** Never cached due to the possibility of nested extension tags */
57
+ regex = new RegExp(String.raw `<!--.*?(?:-->|$)|<${noincludeRegex}(?:\s[^>]*)?/?>|</${noincludeRegex}\s*>|<(${ext})(\s[^>]*?)?(?:/>|>(.*?)</(\1\s*)>)|<(${includeRegex})(\s[^>]*?)?(?:/>|>(.*?)(?:</(${includeRegex}\s*)>|$))`, 'gisu');
53
58
  return wikitext.replace(regex, (substr, name, attr, inner, closing, include, includeAttr, includeInner, includeClosing) => {
54
59
  const l = accum.length;
55
60
  let ch = 'n';
@@ -11,7 +11,9 @@ const heading_1 = require("../src/heading");
11
11
  * @param accum
12
12
  */
13
13
  const parseHrAndDoubleUnderscore = ({ firstChild: { data }, type, name }, config, accum) => {
14
- const { doubleUnderscore } = config, insensitive = new Set(doubleUnderscore[0]), sensitive = new Set(doubleUnderscore[1]);
14
+ const { doubleUnderscore: [insensitive, sensitive, aliases] } = config;
15
+ config.insensitiveDoubleUnderscore ??= new Set(insensitive);
16
+ config.sensitiveDoubleUnderscore ??= new Set(sensitive);
15
17
  config.regexHrAndDoubleUnderscore ??= new RegExp(`__(${[...insensitive, ...sensitive].join('|')})__`, 'giu');
16
18
  if (type !== 'root' && (type !== 'ext-inner' || name !== 'poem')) {
17
19
  data = `\0${data}`;
@@ -21,11 +23,11 @@ const parseHrAndDoubleUnderscore = ({ firstChild: { data }, type, name }, config
21
23
  new hr_1.HrToken(m, config, accum);
22
24
  return `${lead}\0${accum.length - 1}r\x7F`;
23
25
  }).replace(config.regexHrAndDoubleUnderscore, (m, p1) => {
24
- const caseSensitive = sensitive.has(p1), lc = p1.toLowerCase(), caseInsensitive = insensitive.has(lc);
26
+ const caseSensitive = config.sensitiveDoubleUnderscore.has(p1), lc = p1.toLowerCase(), caseInsensitive = config.insensitiveDoubleUnderscore.has(lc);
25
27
  if (caseSensitive || caseInsensitive) {
26
28
  // @ts-expect-error abstract class
27
29
  new doubleUnderscore_1.DoubleUnderscoreToken(p1, caseSensitive, config, accum);
28
- return `\0${accum.length - 1}${caseInsensitive && (doubleUnderscore[2]?.[lc] ?? lc) === 'toc' ? 'u' : 'n'}\x7F`;
30
+ return `\0${accum.length - 1}${caseInsensitive && (aliases?.[lc] ?? lc) === 'toc' ? 'u' : 'n'}\x7F`;
29
31
  }
30
32
  return m;
31
33
  }).replace(/^((?:\0\d+[cn]\x7F)*)(={1,6})(.+)\2((?:\s|\0\d+[cn]\x7F)*)$/gmu, (_, lead, equals, heading, trail) => {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseHtml = void 0;
4
4
  const attributes_1 = require("../src/attributes");
5
5
  const html_1 = require("../src/html");
6
+ const regex = /^(\/?)([a-z][^\s/>]*)((?:\s|\/(?!>))[^>]*?)?(\/?>)([^<]*)$/iu;
6
7
  /**
7
8
  * 解析HTML标签
8
9
  * @param wikitext
@@ -10,11 +11,12 @@ const html_1 = require("../src/html");
10
11
  * @param accum
11
12
  */
12
13
  const parseHtml = (wikitext, config, accum) => {
13
- const regex = /^(\/?)([a-z][^\s/>]*)((?:\s|\/(?!>))[^>]*?)?(\/?>)([^<]*)$/iu, { html } = config, elements = new Set([...html[0], ...html[1], ...html[2]]), bits = wikitext.split('<');
14
+ config.htmlElements ??= new Set(config.html.flat());
15
+ const bits = wikitext.split('<');
14
16
  let text = bits.shift();
15
17
  for (const x of bits) {
16
18
  const mt = regex.exec(x), t = mt?.[2], name = t?.toLowerCase();
17
- if (!mt || !elements.has(name)) {
19
+ if (!mt || !config.htmlElements.has(name)) {
18
20
  text += `<${x}`;
19
21
  continue;
20
22
  }
@@ -11,6 +11,12 @@ const dd_1 = require("../src/nowiki/dd");
11
11
  * @param token 表格节点
12
12
  */
13
13
  const isTr = (token) => token.lastChild.constructor !== index_1.Token;
14
+ /**
15
+ * 取出最近的表格行
16
+ * @param top 当前解析的表格或表格行
17
+ * @param stack 表格栈
18
+ */
19
+ const pop = (top, stack) => top.type === 'td' ? stack.pop() : top;
14
20
  /**
15
21
  * 解析表格,注意`tr`和`td`包含开头的换行
16
22
  * @param {Token & {firstChild: AstText}} root 根节点
@@ -42,9 +48,7 @@ const parseTable = ({ firstChild: { data }, type, name }, config, accum) => {
42
48
  else {
43
49
  lastChild.setText(lastChild.toString() + str);
44
50
  }
45
- },
46
- /** 取出最近的表格行 */
47
- pop = () => top.type === 'td' ? stack.pop() : top;
51
+ };
48
52
  for (const outLine of lines) {
49
53
  top = stack.pop();
50
54
  const [spaces] = /^(?:\s|\0\d+[cno]\x7F)*/u.exec(outLine), line = outLine.slice(spaces.length), matchesStart = /^(:*)((?:\s|\0\d+[cn]\x7F)*)(\{\||\{(?:\0\d+[cn]\x7F)*\0\d+!\x7F|\0\d+\{\x7F)(.*)$/u
@@ -84,7 +88,7 @@ const parseTable = ({ firstChild: { data }, type, name }, config, accum) => {
84
88
  push(attr, stack[stack.length - 1]);
85
89
  }
86
90
  else if (row) {
87
- top = pop();
91
+ top = pop(top, stack);
88
92
  if (top.type === 'tr') {
89
93
  top = stack.pop();
90
94
  }
@@ -94,7 +98,7 @@ const parseTable = ({ firstChild: { data }, type, name }, config, accum) => {
94
98
  top.insertAt(tr);
95
99
  }
96
100
  else {
97
- top = pop();
101
+ top = pop(top, stack);
98
102
  const regex = cell === '!' ? /!!|(?:\||\0\d+!\x7F){2}|\0\d+\+\x7F/gu : /(?:\||\0\d+!\x7F){2}|\0\d+\+\x7F/gu;
99
103
  let mt = regex.exec(attr), lastIndex = 0, lastSyntax = `\n${spaces}${cell}`;
100
104
  /**
package/dist/src/arg.js CHANGED
@@ -71,7 +71,7 @@ class ArgToken extends index_2.Token {
71
71
  if (!this.getAttribute('include')) {
72
72
  const e = (0, lint_1.generateForSelf)(this, { start }, 'no-arg', 'unexpected template argument');
73
73
  if (argDefault) {
74
- e.fix = { range: [start, e.endIndex], text: argDefault.text() };
74
+ e.fix = { range: [start, e.endIndex], text: argDefault.text(), desc: 'expand' };
75
75
  }
76
76
  return [e];
77
77
  }
@@ -98,14 +98,9 @@ class AttributeToken extends index_2.Token {
98
98
  const e = (0, lint_1.generateForChild)(lastChild, rect, 'unclosed-quote', index_1.default.msg('unclosed $1', 'quotes'), 'warning');
99
99
  e.startIndex--;
100
100
  e.startCol--;
101
- const fix = { range: [e.endIndex, e.endIndex], text: this.#quotes[0] };
101
+ const fix = { range: [e.endIndex, e.endIndex], text: this.#quotes[0], desc: 'close' };
102
102
  if (lastChild.childNodes.some(({ type: t, data }) => t === 'text' && /\s/u.test(data))) {
103
- e.suggestions = [
104
- {
105
- desc: 'close',
106
- ...fix,
107
- },
108
- ];
103
+ e.suggestions = [fix];
109
104
  }
110
105
  else {
111
106
  e.fix = fix;
@@ -100,7 +100,7 @@ class AttributesToken extends index_2.Token {
100
100
  const errors = super.lint(start, re), { parentNode, childNodes } = this, attrs = new Map(), duplicated = new Set(), rect = new rect_1.BoundingRect(this, start);
101
101
  if (parentNode?.type === 'html' && parentNode.closing && this.text().trim()) {
102
102
  const e = (0, lint_1.generateForSelf)(this, rect, 'no-ignored', 'attributes of a closing tag');
103
- e.fix = { range: [start, e.endIndex], text: '' };
103
+ e.fix = { range: [start, e.endIndex], text: '', desc: 'remove' };
104
104
  errors.push(e);
105
105
  }
106
106
  for (const attr of childNodes) {
@@ -62,7 +62,7 @@ class ConverterFlagsToken extends index_2.Token {
62
62
  && (variantFlags.size > 0 || !validFlags.has(flag))) {
63
63
  const e = (0, lint_1.generateForChild)(child, rect, 'no-ignored', 'invalid conversion flag');
64
64
  if (variantFlags.size === 0 && definedFlags.has(flag.toUpperCase())) {
65
- e.fix = { range: [e.startIndex, e.endIndex], text: flag.toUpperCase() };
65
+ e.fix = { range: [e.startIndex, e.endIndex], text: flag.toUpperCase(), desc: 'uppercase' };
66
66
  }
67
67
  else {
68
68
  e.suggestions = [
package/dist/src/html.js CHANGED
@@ -125,7 +125,7 @@ class HtmlToken extends index_1.Token {
125
125
  else if (msg === 'tag that is both closing and self-closing') {
126
126
  const { html: [, , voidTags] } = this.getAttribute('config');
127
127
  if (voidTags.includes(this.name)) {
128
- error.fix = { range: [start + 1, start + 2], text: '' };
128
+ error.fix = { range: [start + 1, start + 2], text: '', desc: 'open' };
129
129
  }
130
130
  }
131
131
  errors.push(error);
@@ -107,7 +107,7 @@ class ImageParameterToken extends index_2.Token {
107
107
  const errors = super.lint(start, re), { link, name } = this;
108
108
  if (name === 'invalid') {
109
109
  const e = (0, lint_1.generateForSelf)(this, { start }, 'invalid-gallery', 'invalid image parameter');
110
- e.fix = { range: [start - 1, e.endIndex], text: '' };
110
+ e.fix = { range: [start - 1, e.endIndex], text: '', desc: 'remove' };
111
111
  errors.push(e);
112
112
  }
113
113
  else if (typeof link === 'object' && link.encoded) {
@@ -107,6 +107,7 @@ class LinkBaseToken extends index_2.Token {
107
107
  e.fix = {
108
108
  range: [e.startIndex + textNode.getRelativeIndex() + textNode.data.indexOf('#'), e.endIndex],
109
109
  text: '',
110
+ desc: 'remove',
110
111
  };
111
112
  }
112
113
  errors.push(e);
@@ -76,7 +76,7 @@ class FileToken extends base_1.LinkBaseToken {
76
76
  if (unscaled) {
77
77
  for (const arg of args.filter(({ name }) => name === 'width')) {
78
78
  const e = (0, lint_1.generateForChild)(arg, rect, 'invalid-gallery', 'invalid image parameter');
79
- e.fix = { range: [e.startIndex - 1, e.endIndex], text: '' };
79
+ e.fix = { range: [e.startIndex - 1, e.endIndex], text: '', desc: 'remove' };
80
80
  errors.push(e);
81
81
  }
82
82
  }
@@ -36,7 +36,7 @@ class RedirectTargetToken extends base_1.LinkBaseToken {
36
36
  const e = (0, lint_1.generateForChild)(this.lastChild, { start }, 'no-ignored', 'useless link text');
37
37
  e.startIndex--;
38
38
  e.startCol--;
39
- e.fix = { range: [e.startIndex, e.endIndex], text: '' };
39
+ e.fix = { range: [e.startIndex, e.endIndex], text: '', desc: 'remove' };
40
40
  errors.push(e);
41
41
  }
42
42
  return errors;
@@ -74,7 +74,7 @@ let CommentToken = (() => {
74
74
  return [];
75
75
  }
76
76
  const e = (0, lint_1.generateForSelf)(this, { start }, 'unclosed-comment', index_1.default.msg('unclosed $1', 'HTML comment'));
77
- e.fix = { range: [e.endIndex, e.endIndex], text: '-->' };
77
+ e.fix = { range: [e.endIndex, e.endIndex], text: '-->', desc: 'close' };
78
78
  return [e];
79
79
  }
80
80
  /** @private */
@@ -14,7 +14,7 @@ class NowikiToken extends base_1.NowikiBaseToken {
14
14
  const { name, firstChild: { data } } = this;
15
15
  if ((name === 'templatestyles' || name === 'section') && data) {
16
16
  const e = (0, lint_1.generateForSelf)(this, { start }, 'void-ext', index_1.default.msg('nothing should be in <$1>', name));
17
- e.fix = { range: [start, e.endIndex], text: '' };
17
+ e.fix = { range: [start, e.endIndex], text: '', desc: 'empty' };
18
18
  return [e];
19
19
  }
20
20
  return super.lint(start, new RegExp(String.raw `<\s*(?:/\s*)${name === 'nowiki' ? '' : '?'}(${name})\b`, 'giu'));
@@ -69,7 +69,7 @@ class ParameterToken extends index_2.Token {
69
69
  e.startCol = e.endCol;
70
70
  e.endIndex++;
71
71
  e.endCol++;
72
- e.fix = { range: [e.startIndex, e.endIndex], text: '{{=}}' };
72
+ e.fix = { range: [e.startIndex, e.endIndex], text: '{{=}}', desc: 'escape' };
73
73
  errors.push(e);
74
74
  }
75
75
  return errors;
@@ -111,6 +111,7 @@ class TdToken extends base_1.TableBaseToken {
111
111
  e.fix = {
112
112
  range: [e.startIndex, e.endIndex],
113
113
  text: data.replace(/\|\|/gu, `\n${syntax}`),
114
+ desc: 'newline',
114
115
  };
115
116
  }
116
117
  else {
@@ -193,6 +193,7 @@ class TranscludeToken extends index_2.Token {
193
193
  e.fix = {
194
194
  range: [e.startIndex + textNode.getRelativeIndex() + textNode.data.indexOf('#'), e.endIndex],
195
195
  text: '',
196
+ desc: 'remove',
196
197
  };
197
198
  errors.push(e);
198
199
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikilint",
3
- "version": "2.13.7",
3
+ "version": "2.13.9",
4
4
  "description": "A Node.js linter for MediaWiki markup",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -40,7 +40,7 @@
40
40
  "diff": "bash diff.sh",
41
41
  "diff:stat": "f() { git diff --stat --ignore-all-space --color=always $1 $2 -- . ':!extensions/' ':!bin/' | grep '\\.ts'; }; f",
42
42
  "lint:ts": "tsc --noEmit && eslint --cache .",
43
- "lint:json": "v8r -s config/.schema.json config/*.json",
43
+ "lint:json": "v8r -s config/.schema.json config/*.json && node ./dist/test/json.js",
44
44
  "lint": "npm run lint:ts && npm run lint:json",
45
45
  "prof": "node dist/test/prof.js",
46
46
  "test": "node dist/test/test.js",
@@ -48,10 +48,10 @@
48
48
  "test:real": "node dist/test/real.js"
49
49
  },
50
50
  "dependencies": {
51
+ "@bhsd/common": "^0.4.6",
51
52
  "chalk": "^4.1.2"
52
53
  },
53
54
  "devDependencies": {
54
- "@bhsd/common": "^0.4.6",
55
55
  "@stylistic/eslint-plugin": "^2.11.0",
56
56
  "@stylistic/stylelint-plugin": "^3.1.1",
57
57
  "@types/node": "^22.10.1",