wikilint 2.6.2 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/config/.schema.json +9 -0
  2. package/config/default.json +5 -0
  3. package/config/enwiki.json +3 -0
  4. package/config/llwiki.json +4 -0
  5. package/config/minimum.json +3 -0
  6. package/config/moegirl.json +4 -0
  7. package/config/zhwiki.json +5 -0
  8. package/dist/base.d.ts +4 -2
  9. package/dist/bin/cli.js +1 -1
  10. package/dist/index.d.ts +3 -0
  11. package/dist/internal.d.ts +3 -1
  12. package/dist/lib/element.d.ts +5 -0
  13. package/dist/lib/element.js +35 -1
  14. package/dist/lib/node.d.ts +1 -2
  15. package/dist/lib/node.js +19 -18
  16. package/dist/lib/text.d.ts +1 -0
  17. package/dist/lib/text.js +7 -4
  18. package/dist/lib/title.js +7 -5
  19. package/dist/mixin/attributesParent.d.ts +0 -1
  20. package/dist/mixin/hidden.d.ts +0 -1
  21. package/dist/parser/commentAndExt.js +1 -6
  22. package/dist/parser/converter.js +0 -2
  23. package/dist/parser/externalLinks.js +2 -5
  24. package/dist/parser/links.js +3 -1
  25. package/dist/parser/magicLinks.js +1 -3
  26. package/dist/parser/redirect.js +22 -0
  27. package/dist/src/attribute.js +2 -2
  28. package/dist/src/attributes.js +0 -2
  29. package/dist/src/heading.js +2 -2
  30. package/dist/src/html.js +1 -1
  31. package/dist/src/imageParameter.js +1 -3
  32. package/dist/src/index.d.ts +1 -3
  33. package/dist/src/index.js +11 -0
  34. package/dist/src/link/base.d.ts +1 -1
  35. package/dist/src/link/base.js +1 -1
  36. package/dist/src/link/category.d.ts +6 -1
  37. package/dist/src/link/category.js +4 -1
  38. package/dist/src/link/redirectTarget.d.ts +25 -0
  39. package/dist/src/link/redirectTarget.js +50 -0
  40. package/dist/src/parameter.js +0 -2
  41. package/dist/src/pre.js +1 -1
  42. package/dist/src/redirect.d.ts +26 -0
  43. package/dist/src/redirect.js +46 -0
  44. package/dist/src/syntax.d.ts +2 -3
  45. package/dist/src/table/index.js +2 -3
  46. package/dist/src/table/td.js +2 -2
  47. package/dist/src/transclude.js +2 -2
  48. package/dist/util/string.js +1 -1
  49. package/i18n/zh-hans.json +2 -1
  50. package/i18n/zh-hant.json +2 -1
  51. package/package.json +10 -9
@@ -121,6 +121,14 @@
121
121
  "pattern": "^[-a-z]+$"
122
122
  }
123
123
  },
124
+ "redirection": {
125
+ "description": "magic words for redirection",
126
+ "type": "array",
127
+ "items": {
128
+ "type": "string",
129
+ "pattern": "^#.+$"
130
+ }
131
+ },
124
132
  "variants": {
125
133
  "description": "variants for language conversion",
126
134
  "type": "array",
@@ -166,6 +174,7 @@
166
174
  "protocol",
167
175
  "interwiki",
168
176
  "img",
177
+ "redirection",
169
178
  "variants"
170
179
  ],
171
180
  "additionalProperties": false
@@ -832,6 +832,11 @@
832
832
  "页数=$1": "page",
833
833
  "$1页": "page"
834
834
  },
835
+ "redirection": [
836
+ "#redirect",
837
+ "#重定向",
838
+ "#重新導向"
839
+ ],
835
840
  "variants": [
836
841
  "zh",
837
842
  "zh-hans",
@@ -406,5 +406,8 @@
406
406
  "page=$1": "page",
407
407
  "page $1": "page"
408
408
  },
409
+ "redirection": [
410
+ "#redirect"
411
+ ],
409
412
  "variants": []
410
413
  }
@@ -583,6 +583,10 @@
583
583
  "页数=$1": "page",
584
584
  "$1页": "page"
585
585
  },
586
+ "redirection": [
587
+ "#redirect",
588
+ "#重定向"
589
+ ],
586
590
  "variants": [
587
591
  "zh",
588
592
  "zh-hans",
@@ -132,5 +132,8 @@
132
132
  "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:",
133
133
  "interwiki": [],
134
134
  "img": {},
135
+ "redirection": [
136
+ "#redirect"
137
+ ],
135
138
  "variants": []
136
139
  }
@@ -676,6 +676,10 @@
676
676
  "页数=$1": "page",
677
677
  "$1页": "page"
678
678
  },
679
+ "redirection": [
680
+ "#redirect",
681
+ "#重定向"
682
+ ],
679
683
  "variants": [
680
684
  "zh",
681
685
  "zh-hans",
@@ -790,6 +790,11 @@
790
790
  "頁=$1": "page",
791
791
  "$1頁": "page"
792
792
  },
793
+ "redirection": [
794
+ "#redirect",
795
+ "#重定向",
796
+ "#重新導向"
797
+ ],
793
798
  "variants": [
794
799
  "zh",
795
800
  "zh-hans",
package/dist/base.d.ts CHANGED
@@ -7,9 +7,11 @@ export interface Config {
7
7
  readonly doubleUnderscore: [string[], string[]];
8
8
  readonly protocol: string;
9
9
  readonly img: Record<string, string>;
10
+ readonly redirection: string[];
10
11
  readonly variants: string[];
11
12
  readonly excludes?: string[];
12
13
  }
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' | 'list' | 'dd' | 'converter' | 'converter-flags' | 'converter-flag' | 'converter-rule' | 'converter-rule-variant' | 'converter-rule-to' | 'converter-rule-from' | 'param-line' | 'imagemap-link';
13
15
  export declare const rules: readonly ["bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "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"];
14
16
  export declare namespace LintError {
15
17
  type Severity = 'error' | 'warning';
@@ -45,8 +47,8 @@ export interface AstNode {
45
47
  interface AstElement extends AstNode {
46
48
  }
47
49
  export interface Parser {
48
- config: string | Config;
49
- i18n: string | Record<string, string> | undefined;
50
+ config: Config | string;
51
+ i18n: Record<string, string> | string | undefined;
50
52
  /**
51
53
  * 解析wikitext
52
54
  * @param include 是否嵌入
package/dist/bin/cli.js CHANGED
@@ -118,7 +118,7 @@ for (const file of files) {
118
118
  let start = Infinity;
119
119
  for (const { range: [from, to], text } of fixable) {
120
120
  if (to <= start) {
121
- wikitext = `${wikitext.slice(0, from)}${text}${wikitext.slice(to)}`;
121
+ wikitext = wikitext.slice(0, from) + text + wikitext.slice(to);
122
122
  start = from;
123
123
  }
124
124
  }
package/dist/index.d.ts CHANGED
@@ -19,3 +19,6 @@ declare const Parser: Parser;
19
19
  export = Parser;
20
20
  export type { Config, LintError };
21
21
  export type * from './internal';
22
+ declare global {
23
+ type Acceptable = unknown;
24
+ }
@@ -1,6 +1,8 @@
1
1
  export type { AstNodes, } from './lib/node';
2
2
  export type * from './lib/text';
3
- export type * from './src/index';
3
+ export type { Token } from './src/index';
4
+ export type * from './src/redirect';
5
+ export type * from './src/link/redirectTarget';
4
6
  export type * from './src/onlyinclude';
5
7
  export type * from './src/nowiki/noinclude';
6
8
  export type * from './src/tagPair/include';
@@ -31,6 +31,11 @@ export declare abstract class AstElement extends AstNode {
31
31
  * @param selector 选择器
32
32
  */
33
33
  closest<T = Token>(selector: string): T | undefined;
34
+ /**
35
+ * 符合选择器的第一个后代节点
36
+ * @param selector 选择器
37
+ */
38
+ querySelector<T = Token>(selector: string): T | undefined;
34
39
  /**
35
40
  * 符合选择器的所有后代节点
36
41
  * @param selector 选择器
@@ -20,13 +20,20 @@ class AstElement extends node_1.AstNode {
20
20
  /** 合并相邻的文本子节点 */
21
21
  normalize() {
22
22
  const childNodes = [...this.childNodes];
23
+ /**
24
+ * 移除子节点
25
+ * @param i 移除位置
26
+ */
27
+ const remove = (i) => {
28
+ childNodes.splice(i, 1);
29
+ };
23
30
  for (let i = childNodes.length - 1; i >= 0; i--) {
24
31
  const { type, data } = childNodes[i];
25
32
  if (type !== 'text' || this.getGaps(i - 1)) {
26
33
  //
27
34
  }
28
35
  else if (data === '') {
29
- childNodes.splice(i, 1);
36
+ remove(i);
30
37
  }
31
38
  }
32
39
  this.setAttribute('childNodes', childNodes);
@@ -70,6 +77,33 @@ class AstElement extends node_1.AstNode {
70
77
  }
71
78
  return undefined;
72
79
  }
80
+ /**
81
+ * 符合条件的第一个后代节点
82
+ * @param condition 条件
83
+ */
84
+ #getElementBy(condition) {
85
+ for (const child of this.childNodes) {
86
+ if (child.type === 'text') {
87
+ continue;
88
+ }
89
+ else if (condition(child)) {
90
+ return child;
91
+ }
92
+ const descendant = child.#getElementBy(condition);
93
+ if (descendant) {
94
+ return descendant;
95
+ }
96
+ }
97
+ return undefined;
98
+ }
99
+ /**
100
+ * 符合选择器的第一个后代节点
101
+ * @param selector 选择器
102
+ */
103
+ querySelector(selector) {
104
+ const condition = this.#getCondition(selector);
105
+ return this.#getElementBy(condition);
106
+ }
73
107
  /**
74
108
  * 符合条件的所有后代节点
75
109
  * @param condition 条件
@@ -1,6 +1,5 @@
1
- import type { LintError, AstNode as AstNodeBase } from '../base';
1
+ import type { LintError, AstNode as AstNodeBase, TokenTypes } from '../base';
2
2
  import type { AstText, Token } from '../internal';
3
- import type { TokenTypes } from '../util/constants';
4
3
  export type AstNodes = AstText | Token;
5
4
  export interface Dimension {
6
5
  readonly height: number;
package/dist/lib/node.js CHANGED
@@ -1,6 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AstNode = void 0;
4
+ /**
5
+ * 计算字符串的行列数
6
+ * @param str 字符串
7
+ */
8
+ const getDimension = (str) => {
9
+ const lines = str.split('\n'), height = lines.length;
10
+ return { height, width: lines[height - 1].length };
11
+ };
12
+ /**
13
+ * 获取子节点相对于父节点的字符位置
14
+ * @param j 子节点序号
15
+ * @param parent 父节点
16
+ */
17
+ const getIndex = (j, parent) => parent.childNodes.slice(0, j).reduce((acc, cur, i) => acc + String(cur).length + parent.getGaps(i), 0)
18
+ + parent.getAttribute('padding');
4
19
  /** 类似Node */
5
20
  class AstNode {
6
21
  childNodes = [];
@@ -66,15 +81,14 @@ class AstNode {
66
81
  posFromIndex(index) {
67
82
  const str = String(this);
68
83
  if (index >= -str.length && index <= str.length) {
69
- const lines = str.slice(0, index).split('\n'), top = lines.length - 1;
70
- return { top, left: lines[top].length };
84
+ const { height, width } = getDimension(str.slice(0, index));
85
+ return { top: height - 1, left: width };
71
86
  }
72
87
  return undefined;
73
88
  }
74
89
  /** 获取行数和最后一行的列数 */
75
90
  #getDimension() {
76
- const lines = String(this).split('\n'), height = lines.length;
77
- return { height, width: lines[height - 1].length };
91
+ return getDimension(String(this));
78
92
  }
79
93
  /** @private */
80
94
  getGaps(i) {
@@ -85,23 +99,10 @@ class AstNode {
85
99
  * @param j 子节点序号
86
100
  */
87
101
  getRelativeIndex(j) {
88
- let childNodes;
89
- /**
90
- * 获取子节点相对于父节点的字符位置,使用前需要先给`childNodes`赋值
91
- * @param end 子节点序号
92
- * @param parent 父节点
93
- */
94
- const getIndex = (end, parent) => childNodes.slice(0, end).reduce((acc, cur, i) => acc + String(cur).length + parent.getGaps(i), 0)
95
- + parent.getAttribute('padding');
96
102
  if (j === undefined) {
97
103
  const { parentNode } = this;
98
- if (parentNode) {
99
- ({ childNodes } = parentNode);
100
- return getIndex(childNodes.indexOf(this), parentNode);
101
- }
102
- return 0;
104
+ return parentNode ? getIndex(parentNode.childNodes.indexOf(this), parentNode) : 0;
103
105
  }
104
- ({ childNodes } = this);
105
106
  return getIndex(j, this);
106
107
  }
107
108
  /** 获取当前节点的绝对位置 */
@@ -13,6 +13,7 @@ export declare class AstText extends AstNode {
13
13
  /**
14
14
  * @override
15
15
  * @param start
16
+ * @throws `Error` 孤立文本节点
16
17
  */
17
18
  lint(start?: number, errorRegex?: RegExp): LintError[];
18
19
  /**
package/dist/lib/text.js CHANGED
@@ -1,10 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AstText = void 0;
4
+ const string_1 = require("../util/string");
4
5
  const index_1 = require("../index");
5
6
  const node_1 = require("./node");
6
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
7
- /<\s*(?:\/\s*)?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*?\])|((?:^|\])[^[]*?)\]+|https?[:/]\/+/giu;
8
7
  const source = '<\\s*(?:\\/\\s*)?([a-z]\\w*)' // 疑似标签
9
8
  + '|'
10
9
  + '\\{+|\\}+' // `{`、`}`
@@ -12,7 +11,7 @@ const source = '<\\s*(?:\\/\\s*)?([a-z]\\w*)' // 疑似标签
12
11
  + '\\[{2,}|\\[(?![^[]*?\\])' // `[`
13
12
  + '|'
14
13
  + '((?:^|\\])[^[]*?)\\]+', // `]`
15
- errorSyntax = new RegExp(`${source}|https?[:/]\\/+`, 'giu'), errorSyntaxUrl = new RegExp(source, 'giu'), regexes = {
14
+ errorSyntax = new RegExp(`${source}|https?[:/]\\/+`, 'giu'), errorSyntaxUrl = new RegExp(source, 'giu'), extImage = new RegExp(`^https?:\\/\\/${string_1.extUrlCharFirst}${string_1.extUrlChar}\\.(?:gif|png|jpg|jpeg)$`, 'iu'), regexes = {
16
15
  '[': /[[\]]/u,
17
16
  '{': /[{}]/u,
18
17
  ']': /[[\]](?=[^[\]]*$)/u,
@@ -77,11 +76,12 @@ class AstText extends node_1.AstNode {
77
76
  /**
78
77
  * @override
79
78
  * @param start
79
+ * @throws `Error` 孤立文本节点
80
80
  */
81
81
  lint(start = this.getAbsoluteIndex(), errorRegex) {
82
82
  const { data, parentNode, nextSibling, previousSibling } = this;
83
83
  if (!parentNode) {
84
- return [];
84
+ throw new Error('无法对孤立文本节点进行语法分析!');
85
85
  }
86
86
  const { type, name, parentNode: grandparent } = parentNode;
87
87
  let isHtmlAttrVal = false;
@@ -126,6 +126,9 @@ class AstText extends node_1.AstNode {
126
126
  else if (char === ']' && (index || length > 1)) {
127
127
  errorRegex.lastIndex--;
128
128
  }
129
+ else if (char === 'h' && index === 0 && type === 'ext-link-text' && extImage.test(data)) {
130
+ continue;
131
+ }
129
132
  const startIndex = start + index, endIndex = startIndex + length, rootStr = String(root), nextChar = rootStr[endIndex], previousChar = rootStr[startIndex - 1], severity = length > 1 && !(char === '<' && !/[\s/>]/u.test(nextChar ?? '')
130
133
  || isHtmlAttrVal && (char === '[' || char === ']'))
131
134
  || char === '{' && (nextChar === char || previousChar === '-')
package/dist/lib/title.js CHANGED
@@ -23,18 +23,18 @@ class Title {
23
23
  }
24
24
  set main(title) {
25
25
  title = title.replace(/_/gu, ' ').trim();
26
- this.#main = title && `${title[0].toUpperCase()}${title.slice(1)}`;
26
+ this.#main = title && title[0].toUpperCase() + title.slice(1);
27
27
  }
28
28
  /** 命名空间前缀 */
29
29
  get prefix() {
30
30
  const namespace = this.#namespaces[this.ns];
31
- return `${namespace}${namespace && ':'}`;
31
+ return namespace + (namespace && ':');
32
32
  }
33
33
  /** 完整标题 */
34
34
  get title() {
35
- const prefix = `${this.interwiki}${this.interwiki && ':'}${this.prefix}`;
35
+ const prefix = this.interwiki + (this.interwiki && ':') + this.prefix;
36
36
  // eslint-disable-next-line prefer-const
37
- let title = `${prefix}${this.main}`.replace(/ /gu, '_');
37
+ let title = (prefix + this.main).replace(/ /gu, '_');
38
38
  return title;
39
39
  }
40
40
  /** 扩展名 */
@@ -103,7 +103,9 @@ class Title {
103
103
  }
104
104
  /** @private */
105
105
  toString() {
106
- return `${this.title}${this.fragment === undefined ? '' : `#${this.fragment}`}`;
106
+ return `${this.title}${this.fragment === undefined
107
+ ? ''
108
+ : `#${this.fragment}`}`;
107
109
  }
108
110
  }
109
111
  exports.Title = Title;
@@ -11,4 +11,3 @@ export interface AttributesParentBase {
11
11
  * @param constructor 基类
12
12
  * @param _ context
13
13
  */
14
- export declare const attributesParent: (i?: number) => <T extends AstConstructor>(constructor: T, _?: unknown) => T;
@@ -2,4 +2,3 @@
2
2
  * 解析后不可见的类
3
3
  * @param constructor 基类
4
4
  */
5
- export declare const hiddenToken: <T extends AstConstructor>(constructor: T) => T;
@@ -49,14 +49,9 @@ const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
49
49
  return str;
50
50
  }
51
51
  }
52
- /* eslint-disable @typescript-eslint/no-unused-expressions */
53
- /<foo(?:\s[^>]*)?>|<\/foo\s*>/giu;
54
- /<(bar)(\s[^>]*?)?(?:\/>|>(.*?)<\/(\1\s*)>)/gisu;
55
- /<(baz)(\s[^>]*?)?(?:\/>|>(.*?)(?:<\/(baz\s*)>|$))/gisu;
56
- /* eslint-enable @typescript-eslint/no-unused-expressions */
57
52
  const ext = config.ext.join('|'), noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly', regex = new RegExp('<!--.*?(?:-->|$)' // comment
58
53
  + '|'
59
- + `<${noincludeRegex}(?:\\s[^>]*)?>|</${noincludeRegex}\\s*>` // <noinclude>
54
+ + `<${noincludeRegex}(?:\\s[^>]*)?/?>|</${noincludeRegex}\\s*>` // <noinclude>
60
55
  + '|'
61
56
  + `<(${ext})(\\s[^>]*?)?(?:/>|>(.*?)</(\\1\\s*)>)` // 扩展标签
62
57
  + '|'
@@ -14,8 +14,6 @@ const parseConverter = (text, config, accum) => {
14
14
  while (mt) {
15
15
  const { 0: syntax, index } = mt;
16
16
  if (syntax === '}-') {
17
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
18
- /;(?=(?:[^;]*?=>)?\s*foo\s*:|(?:\s|\0\d+c\x7F)*$)/u;
19
17
  const top = stack.pop(), { length } = accum, str = text.slice(top.index + 2, index), i = str.indexOf('|'), [flags, raw] = i === -1 ? [[], str] : [str.slice(0, i).split(';'), str.slice(i + 1)], temp = raw.replace(/(&[#a-z\d]+);/giu, '$1\x01'), variants = `(?:${config.variants.join('|')})`, rules = temp.split(new RegExp(`;(?=(?:[^;]*?=>)?\\s*${variants}\\s*:|(?:\\s|\0\\d+c\x7F)*$)`, 'u'))
20
18
  .map(rule => rule.replace(/\x01/gu, ';'));
21
19
  // @ts-expect-error abstract class
@@ -12,8 +12,6 @@ const magicLink_1 = require("../src/magicLink");
12
12
  * @param inFile 是否在图链中
13
13
  */
14
14
  const parseExternalLinks = (wikitext, config, accum, inFile) => {
15
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
16
- /\[((?:\[[\da-f:.]+\]|[^[\]\t\n\p{Zs}])[^[\]\t\n\p{Zs}]*(?=[[\]\t\p{Zs}]|\0\d))(\p{Zs}*(?=\P{Zs}))([^\]\n]*)\]/giu;
17
15
  const regex = new RegExp('\\[' // 左括号
18
16
  + `(${'\0\\d+f\x7F' // 预先解析的MagicLinkToken
19
17
  + '|'
@@ -23,12 +21,11 @@ const parseExternalLinks = (wikitext, config, accum, inFile) => {
23
21
  + '\\]', // 右括号
24
22
  'giu');
25
23
  return wikitext.replace(regex, (_, url, space, text) => {
26
- const { length } = accum;
27
- const mt = /&[lg]t;/u.exec(url);
24
+ const { length } = accum, mt = /&[lg]t;/u.exec(url);
28
25
  if (mt) {
29
26
  url = url.slice(0, mt.index);
30
27
  space = '';
31
- text = `${url.slice(mt.index)}${space}${text}`;
28
+ text = url.slice(mt.index) + space + text;
32
29
  }
33
30
  if (inFile) {
34
31
  // @ts-expect-error abstract class
@@ -14,7 +14,9 @@ const category_1 = require("../src/link/category");
14
14
  * @param accum
15
15
  */
16
16
  const parseLinks = (wikitext, config, accum) => {
17
- const regex = /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(?:(\||\0\d+!\x7F)(.*?[^\]]))?\]\](.*)$/su, regexImg = /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(\||\0\d+!\x7F)(.*)$/su, regexExt = new RegExp(`^\\s*(?:${config.protocol}|//)`, 'iu'), bits = wikitext.split('[[');
17
+ const regex = true // eslint-disable-line no-constant-condition, @typescript-eslint/no-unnecessary-condition
18
+ ? /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(?:(\||\0\d+!\x7F)(.*?[^\]]))?\]\](.*)$/su
19
+ : /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(?:(\||\0\d+!\x7F)(.*?[^\]])?)?\]\](.*)$/su, regexImg = /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(\||\0\d+!\x7F)(.*)$/su, regexExt = new RegExp(`^\\s*(?:${config.protocol}|//)`, 'iu'), bits = wikitext.split('[[');
18
20
  let s = bits.shift();
19
21
  for (let i = 0; i < bits.length; i++) {
20
22
  let mightBeImg = false, link, delimiter, text, after;
@@ -10,8 +10,6 @@ const magicLink_1 = require("../src/magicLink");
10
10
  * @param accum
11
11
  */
12
12
  const parseMagicLinks = (wikitext, config, accum) => {
13
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
14
- /(^|[^\p{L}\d_])((?:\[[\da-f:.]+\]|[^[\]<>"\t\n\p{Zs}])(?:[^[\]<>"\0\t\n\p{Zs}]|\0\d+c\x7F)*)/giu;
15
13
  const regex = new RegExp(`(^|[^\\p{L}\\d_])(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})`, 'giu');
16
14
  return wikitext.replace(regex, (m, lead, p1) => {
17
15
  let trail = '', url = lead ? m.slice(lead.length) : m;
@@ -26,7 +24,7 @@ const parseMagicLinks = (wikitext, config, accum) => {
26
24
  if (sepChars[0].startsWith(';') && /&(?:[a-z]+|#x[\da-f]+|#\d+)$/iu.test(url.slice(0, sepChars.index))) {
27
25
  correction = 1;
28
26
  }
29
- trail = `${url.slice(sepChars.index + correction)}${trail}`;
27
+ trail = url.slice(sepChars.index + correction) + trail;
30
28
  url = url.slice(0, sepChars.index + correction);
31
29
  }
32
30
  if (trail.length >= p1.length) {
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseRedirect = void 0;
4
+ const index_1 = require("../index");
5
+ const redirect_1 = require("../src/redirect");
6
+ /**
7
+ * 解析重定向
8
+ * @param text
9
+ * @param config
10
+ * @param accum
11
+ */
12
+ const parseRedirect = (text, config, accum) => {
13
+ const re = new RegExp(`^(\\s*)((?:${config.redirection.join('|')})\\s*(?::\\s*)?)\\[\\[([^\\n|\\]]+)(\\|.*?)?\\]\\](\\s*)`, 'iu'), mt = re.exec(text);
14
+ if (mt && index_1.default.normalizeTitle(mt[3], 0, false, config, true, true).valid) {
15
+ text = `\0${accum.length}c\x7F${text.slice(mt[0].length)}`;
16
+ // @ts-expect-error abstract class
17
+ new redirect_1.RedirectToken(...mt.slice(1), config, accum);
18
+ return text;
19
+ }
20
+ return false;
21
+ };
22
+ exports.parseRedirect = parseRedirect;
@@ -244,7 +244,7 @@ class AttributeToken extends index_2.Token {
244
244
  /** @private */
245
245
  toString() {
246
246
  const [quoteStart = '', quoteEnd = ''] = this.#quotes;
247
- return this.#equal ? `${super.toString(`${this.#equal}${quoteStart}`)}${quoteEnd}` : String(this.firstChild);
247
+ return this.#equal ? super.toString(this.#equal + quoteStart) + quoteEnd : String(this.firstChild);
248
248
  }
249
249
  /** @override */
250
250
  text() {
@@ -268,7 +268,7 @@ class AttributeToken extends index_2.Token {
268
268
  range: [e.endIndex, e.endIndex],
269
269
  text: this.#quotes[0],
270
270
  };
271
- if (lastChild.childNodes.some(child => child.type === 'text' && /\s/u.test(child.text()))) {
271
+ if (lastChild.childNodes.some(({ type: t, data }) => t === 'text' && /\s/u.test(data))) {
272
272
  e.suggestions = [
273
273
  {
274
274
  desc: 'close',
@@ -32,8 +32,6 @@ class AttributesToken extends index_2.Token {
32
32
  this.type = type;
33
33
  this.setAttribute('name', name);
34
34
  if (attr) {
35
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
36
- /([^\s/](?:(?!\0\d+~\x7F)[^\s/=])*)(?:(\s*(?:=|\0\d+~\x7F)\s*)(?:(["'])(.*?)(\3|$)|(\S*)))?/gsu;
37
35
  const regex = new RegExp('([^\\s/](?:(?!\0\\d+~\x7F)[^\\s/=])*)' // 属性名
38
36
  + `(?:${'((?:\\s|\0\\d+c\x7F)*(?:=|\0\\d+~\x7F)(?:\\s|\0\\d+c\x7F)*)' // `=`和前后的空白字符
39
37
  + `(?:(["'])(.*?)(\\3|$)|(\\S*))` // 属性值
@@ -36,12 +36,12 @@ class HeadingToken extends index_2.Token {
36
36
  /** @private */
37
37
  toString() {
38
38
  const equals = this.#equals;
39
- return `${equals}${String(this.firstChild)}${equals}${String(this.lastChild)}`;
39
+ return equals + String(this.firstChild) + equals + String(this.lastChild);
40
40
  }
41
41
  /** @override */
42
42
  text() {
43
43
  const equals = this.#equals;
44
- return `${equals}${this.firstChild.text()}${equals}`;
44
+ return equals + this.firstChild.text() + equals;
45
45
  }
46
46
  /** @private */
47
47
  getAttribute(key) {
package/dist/src/html.js CHANGED
@@ -69,7 +69,7 @@ class HtmlToken extends index_2.Token {
69
69
  }
70
70
  /** @override */
71
71
  text() {
72
- const { closing, } = this, tag = `${this.#tag}${closing ? '' : super.text()}`;
72
+ const { closing, } = this, tag = this.#tag + (closing ? '' : super.text());
73
73
  return `<${closing ? '/' : ''}${tag}${this.#selfClosing ? '/' : ''}>`;
74
74
  }
75
75
  /** @private */
@@ -16,8 +16,6 @@ function validate(key, val, config, halfParsed, ext) {
16
16
  if (!value) {
17
17
  return val;
18
18
  }
19
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
20
- /^(?:\/\/(?:\[[\da-f:.]+\]|[^[\]<>"\t\n\p{Zs}])|\0\d+m\x7F)(?:[^[\]<>"\0\t\n\p{Zs}]|\0\d+c\x7F)*$/iu;
21
19
  const regex = new RegExp(`^(?:(?:${config.protocol}|//)${string_1.extUrlCharFirst}|\0\\d+m\x7F)${string_1.extUrlChar}$`, 'iu');
22
20
  if (regex.test(value)) {
23
21
  return val;
@@ -69,7 +67,7 @@ class ImageParameterToken extends index_2.Token {
69
67
  }
70
68
  else {
71
69
  super(mt[2], config, accum, {});
72
- this.#syntax = `${mt[1]}${param[0]}${mt[3]}`;
70
+ this.#syntax = mt[1] + param[0] + mt[3];
73
71
  }
74
72
  this.setAttribute('name', param[1]);
75
73
  return;
@@ -1,11 +1,9 @@
1
- import { BuildMethod } from '../util/constants';
2
1
  import Parser from '../index';
3
2
  import { AstElement } from '../lib/element';
4
3
  import { AstText } from '../lib/text';
5
- import type { LintError } from '../base';
4
+ import type { LintError, TokenTypes } from '../base';
6
5
  import type { Title } from '../lib/title';
7
6
  import type { AstNodes } from '../internal';
8
- import type { TokenTypes } from '../util/constants';
9
7
  /**
10
8
  * 所有节点的基类
11
9
  * @classdesc `{childNodes: ...(AstText|Token)}`
package/dist/src/index.js CHANGED
@@ -80,6 +80,8 @@ class Token extends element_1.AstElement {
80
80
  case 0:
81
81
  if (this.type === 'root') {
82
82
  this.#accum.shift();
83
+ const isRedirect = this.#parseRedirect();
84
+ include &&= !isRedirect;
83
85
  }
84
86
  this.#include = include;
85
87
  this.#parseCommentAndExt(include);
@@ -176,6 +178,15 @@ class Token extends element_1.AstElement {
176
178
  }
177
179
  return this;
178
180
  }
181
+ /** 解析重定向 */
182
+ #parseRedirect() {
183
+ const { parseRedirect } = require('../parser/redirect');
184
+ const wikitext = String(this.firstChild), parsed = parseRedirect(wikitext, this.#config, this.#accum);
185
+ if (parsed) {
186
+ this.setText(parsed);
187
+ }
188
+ return Boolean(parsed);
189
+ }
179
190
  /**
180
191
  * 解析HTML注释和扩展标签
181
192
  * @param includeOnly 是否嵌入
@@ -9,7 +9,7 @@ import type { Title } from '../../lib/title';
9
9
  */
10
10
  export declare abstract class LinkBaseToken extends Token {
11
11
  #private;
12
- type: 'link' | 'category' | 'file' | 'gallery-image' | 'imagemap-image';
12
+ type: 'link' | 'category' | 'file' | 'gallery-image' | 'imagemap-image' | 'redirect-target';
13
13
  readonly name: string;
14
14
  readonly childNodes: readonly [AtomToken, ...Token[]];
15
15
  abstract get firstChild(): AtomToken;
@@ -101,7 +101,7 @@ class LinkBaseToken extends index_2.Token {
101
101
  errors.push(e);
102
102
  }
103
103
  }
104
- if (linkType !== 'link' && fragment !== undefined) {
104
+ if (linkType !== 'link' && linkType !== 'redirect-target' && fragment !== undefined) {
105
105
  rect ??= { start, ...this.getRootNode().posFromIndex(start) };
106
106
  const e = (0, lint_1.generateForChild)(target, rect, 'no-ignored', 'useless fragment'), textNode = target.childNodes.find((c) => c.type === 'text' && c.data.includes('#'));
107
107
  if (textNode) {
@@ -1,5 +1,10 @@
1
1
  import { LinkBaseToken } from './base';
2
- /** 分类 */
2
+ import type { Token, AtomToken } from '../../internal';
3
+ /**
4
+ * 分类
5
+ * @classdesc `{childNodes: [AtomToken, ?Token]}`
6
+ */
3
7
  export declare abstract class CategoryToken extends LinkBaseToken {
4
8
  readonly type = "category";
9
+ readonly childNodes: readonly [AtomToken] | readonly [AtomToken, Token];
5
10
  }
@@ -2,7 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CategoryToken = void 0;
4
4
  const base_1 = require("./base");
5
- /** 分类 */
5
+ /**
6
+ * 分类
7
+ * @classdesc `{childNodes: [AtomToken, ?Token]}`
8
+ */
6
9
  class CategoryToken extends base_1.LinkBaseToken {
7
10
  type = 'category';
8
11
  }
@@ -0,0 +1,25 @@
1
+ import Parser from '../../index';
2
+ import { LinkBaseToken } from './base';
3
+ import { NoincludeToken } from '../nowiki/noinclude';
4
+ import type { LintError } from '../../base';
5
+ import type { Title } from '../../lib/title';
6
+ import type { Token, AtomToken } from '../../internal';
7
+ /**
8
+ * 重定向目标
9
+ * @classdesc `{childNodes: [AtomToken, ?Token]}`
10
+ */
11
+ export declare abstract class RedirectTargetToken extends LinkBaseToken {
12
+ readonly type = "redirect-target";
13
+ readonly childNodes: readonly [AtomToken] | readonly [AtomToken, NoincludeToken];
14
+ abstract get lastChild(): AtomToken | NoincludeToken;
15
+ /**
16
+ * @param link 链接标题
17
+ * @param linkText 链接显示文字
18
+ * @param delimiter `|`
19
+ */
20
+ constructor(link: string, linkText?: string, config?: Parser.Config, accum?: Token[]);
21
+ /** @override */
22
+ text(): string;
23
+ /** @override */
24
+ lint(start?: number): LintError[];
25
+ }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedirectTargetToken = void 0;
4
+ const lint_1 = require("../../util/lint");
5
+ const index_1 = require("../../index");
6
+ const base_1 = require("./base");
7
+ const noinclude_1 = require("../nowiki/noinclude");
8
+ /**
9
+ * 重定向目标
10
+ * @classdesc `{childNodes: [AtomToken, ?Token]}`
11
+ */
12
+ class RedirectTargetToken extends base_1.LinkBaseToken {
13
+ type = 'redirect-target';
14
+ /**
15
+ * @param link 链接标题
16
+ * @param linkText 链接显示文字
17
+ * @param delimiter `|`
18
+ */
19
+ constructor(link, linkText, config = index_1.default.getConfig(), accum = []) {
20
+ super(link, undefined, config, accum);
21
+ if (linkText !== undefined) {
22
+ // @ts-expect-error abstract class
23
+ this.insertAt(new noinclude_1.NoincludeToken(linkText, config, accum));
24
+ }
25
+ }
26
+ /** @private */
27
+ getTitle() {
28
+ return this.normalizeTitle(String(this.firstChild), 0, true, true);
29
+ }
30
+ /** @override */
31
+ text() {
32
+ return `[[${String(this.firstChild)}]]`;
33
+ }
34
+ /** @override */
35
+ lint(start = this.getAbsoluteIndex()) {
36
+ const errors = super.lint(start, /\]\]/u);
37
+ if (this.length === 2) {
38
+ const e = (0, lint_1.generateForChild)(this.lastChild, { start }, 'no-ignored', 'useless link text');
39
+ e.startIndex--;
40
+ e.startCol--;
41
+ e.fix = {
42
+ range: [e.startIndex, e.endIndex],
43
+ text: '',
44
+ };
45
+ errors.push(e);
46
+ }
47
+ return errors;
48
+ }
49
+ }
50
+ exports.RedirectTargetToken = RedirectTargetToken;
@@ -56,8 +56,6 @@ class ParameterToken extends index_2.Token {
56
56
  }
57
57
  /** @override */
58
58
  lint(start = this.getAbsoluteIndex(), re) {
59
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
60
- /https?:\/\/(?:\[[\da-f:.]+\]|[^[\]<>"\t\n\p{Zs}])(?:[^[\]<>"\0\t\n\p{Zs}]|\0\d+c\x7F)*$/iu;
61
59
  const errors = super.lint(start, re), { firstChild } = this, link = new RegExp(`https?://${string_1.extUrlCharFirst}${string_1.extUrlChar}$`, 'iu').exec(firstChild.text())?.[0];
62
60
  if (link && new URL(link).search) {
63
61
  const e = (0, lint_1.generateForChild)(firstChild, { start }, 'unescaped', 'unescaped query string in an anonymous parameter');
package/dist/src/pre.js CHANGED
@@ -26,7 +26,7 @@ class PreToken extends index_2.Token {
26
26
  i = wikitext.indexOf(opening);
27
27
  j = wikitext.indexOf(closing, i + length);
28
28
  }
29
- wikitext = `${str}${wikitext}`;
29
+ wikitext = str + wikitext;
30
30
  }
31
31
  super(wikitext, config, accum, {});
32
32
  this.setAttribute('stage', constants_1.MAX_STAGE - 1);
@@ -0,0 +1,26 @@
1
+ import Parser from '../index';
2
+ import { Token } from './index';
3
+ import { SyntaxToken } from './syntax';
4
+ import { RedirectTargetToken } from './link/redirectTarget';
5
+ import type { LintError } from '../base';
6
+ export declare abstract class RedirectToken extends Token {
7
+ #private;
8
+ readonly type = "redirect";
9
+ readonly childNodes: readonly [SyntaxToken, RedirectTargetToken];
10
+ abstract get firstChild(): SyntaxToken;
11
+ abstract get lastChild(): RedirectTargetToken;
12
+ abstract get previousSibling(): undefined;
13
+ /**
14
+ * @param pre leading whitespace
15
+ * @param syntax 重定向魔术字
16
+ * @param link 重定向目标
17
+ * @param text 重定向显示文本(无效)
18
+ * @param post trailing whitespace
19
+ */
20
+ constructor(pre: string, syntax: string | undefined, link: string, text: string | undefined, post: string, config?: Parser.Config, accum?: Token[]);
21
+ /** @override */
22
+ toString(): string;
23
+ /** @override */
24
+ lint(start?: number): LintError[];
25
+ }
26
+ export {};
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedirectToken = void 0;
4
+ const hidden_1 = require("../mixin/hidden");
5
+ const index_1 = require("../index");
6
+ const index_2 = require("./index");
7
+ const syntax_1 = require("./syntax");
8
+ const redirectTarget_1 = require("./link/redirectTarget");
9
+ /**
10
+ * 重定向
11
+ * @classdesc `{childNodes: [SyntaxToken, LinkToken]}`
12
+ */
13
+ class RedirectToken extends (0, hidden_1.hiddenToken)(index_2.Token) {
14
+ type = 'redirect';
15
+ #pre;
16
+ #post;
17
+ /**
18
+ * @param pre leading whitespace
19
+ * @param syntax 重定向魔术字
20
+ * @param link 重定向目标
21
+ * @param text 重定向显示文本(无效)
22
+ * @param post trailing whitespace
23
+ */
24
+ constructor(pre, syntax, link, text, post, config = index_1.default.getConfig(), accum = []) {
25
+ super(undefined, config, accum);
26
+ this.#pre = pre;
27
+ this.#post = post;
28
+ const pattern = new RegExp(`^(?:${config.redirection.join('|')})\\s*(?::\\s*)?$`, 'iu');
29
+ this.append(new syntax_1.SyntaxToken(syntax, pattern, 'redirect-syntax', config, accum, {}),
30
+ // @ts-expect-error abstract class
31
+ new redirectTarget_1.RedirectTargetToken(link, text?.slice(1), config, accum));
32
+ }
33
+ /** @private */
34
+ getAttribute(key) {
35
+ return key === 'padding' ? this.#pre.length : super.getAttribute(key);
36
+ }
37
+ /** @override */
38
+ toString() {
39
+ return this.#pre + super.toString() + this.#post;
40
+ }
41
+ /** @override */
42
+ lint(start = this.getAbsoluteIndex()) {
43
+ return this.lastChild.lint(start + this.#pre.length + String(this.firstChild).length);
44
+ }
45
+ }
46
+ exports.RedirectToken = RedirectToken;
@@ -1,13 +1,12 @@
1
1
  import Parser from '../index';
2
2
  import { Token } from './index';
3
- import type { LintError } from '../base';
4
- declare type SyntaxTypes = 'plain' | 'heading-trail' | 'magic-word-name' | 'table-syntax';
3
+ declare type SyntaxTypes = 'plain' | 'heading-trail' | 'magic-word-name' | 'table-syntax' | 'redirect-syntax';
5
4
  /** 满足特定语法格式的plain Token */
6
5
  export declare class SyntaxToken extends Token {
7
6
  type: SyntaxTypes;
8
7
  /** @param pattern 语法正则 */
9
8
  constructor(wikitext: string | undefined, pattern: RegExp, type?: SyntaxTypes, config?: Parser.Config, accum?: Token[], acceptable?: Acceptable);
10
9
  /** @override */
11
- lint(): LintError[];
10
+ lint(): [];
12
11
  }
13
12
  export {};
@@ -132,10 +132,9 @@ class TableToken extends trBase_1.TrBaseToken {
132
132
  getNthCell(
133
133
  // eslint-disable-next-line @stylistic/comma-dangle
134
134
  coords) {
135
+ // eslint-disable-next-line prefer-const, @typescript-eslint/no-unnecessary-type-assertion
136
+ let rawCoords = coords;
135
137
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
136
- const rawCoords = coords.row === undefined
137
- ? undefined
138
- : coords;
139
138
  return rawCoords && this.getNthRow(rawCoords.row, false, false)?.getNthCol(rawCoords.column);
140
139
  }
141
140
  getNthRow(n, force, insert) {
@@ -72,12 +72,12 @@ class TdToken extends base_1.TableBaseToken {
72
72
  /** @private */
73
73
  toString() {
74
74
  const { childNodes: [syntax, attr, inner] } = this;
75
- return `${String(syntax)}${String(attr)}${this.#innerSyntax}${String(inner)}`;
75
+ return String(syntax) + String(attr) + this.#innerSyntax + String(inner);
76
76
  }
77
77
  /** @override */
78
78
  text() {
79
79
  const { childNodes: [syntax, attr, inner] } = this;
80
- return `${syntax.text()}${attr.text()}${this.#innerSyntax}${inner.text()}`;
80
+ return syntax.text() + attr.text() + this.#innerSyntax + inner.text();
81
81
  }
82
82
  /** @private */
83
83
  getGaps(i) {
@@ -140,7 +140,7 @@ class TranscludeToken extends index_2.Token {
140
140
  return type === 'magic-word' && name === 'vardefine'
141
141
  ? ''
142
142
  : `{{${modifier}${type === 'magic-word'
143
- ? `${firstChild.text()}${length === 1 ? '' : ':'}${(0, string_1.text)(childNodes.slice(1), '|')}`
143
+ ? firstChild.text() + (length === 1 ? '' : ':') + (0, string_1.text)(childNodes.slice(1), '|')
144
144
  : super.text('|')}}}`;
145
145
  }
146
146
  /** @private */
@@ -184,7 +184,7 @@ class TranscludeToken extends index_2.Token {
184
184
  errors.push((0, lint_1.generateForSelf)(this, rect, 'invalid-invoke', 'missing module function'));
185
185
  return errors;
186
186
  }
187
- const duplicatedArgs = this.getDuplicatedArgs();
187
+ const duplicatedArgs = this.getDuplicatedArgs().filter(([, parameter]) => !parameter[0].querySelector('ext'));
188
188
  if (duplicatedArgs.length > 0) {
189
189
  rect ??= { start, ...this.getRootNode().posFromIndex(start) };
190
190
  errors.push(...duplicatedArgs.flatMap(([, args]) => args).map(arg => (0, lint_1.generateForChild)(arg, rect, 'no-duplicate', 'duplicated parameter')));
@@ -26,7 +26,7 @@ exports.text = text;
26
26
  const names = { lt: '<', gt: '>', lbrack: '[', rbrack: ']', lbrace: '{', rbrace: '}' };
27
27
  /** decode HTML entities */
28
28
  exports.decodeHtml = factory(/&(?:#(\d+|x[\da-fA-F]+)|([lLgG][tT]|[lr]brac[ke]));/gu, (_, code, name) => code
29
- ? String.fromCodePoint(Number(`${/^x/iu.test(code) ? '0' : ''}${code}`))
29
+ ? String.fromCodePoint(Number((/^x/iu.test(code) ? '0' : '') + code))
30
30
  : names[name.toLowerCase()]);
31
31
  /** escape newlines */
32
32
  exports.noWrap = factory(/\n/gu, '\\n');
package/i18n/zh-hans.json CHANGED
@@ -51,7 +51,8 @@
51
51
  "unexpected template argument": "未预期的模板参数",
52
52
  "unmatched closing tag": "未匹配的闭合标签",
53
53
  "unnecessary URL encoding in an internal link": "内链中不必要的URL编码",
54
- "useless fragment": "多余的fragment",
54
+ "useless fragment": "无用的fragment",
55
+ "useless link text": "无用的链接文字",
55
56
  "variable anchor in a section header": "段落标题中可变的锚点",
56
57
  "vertical-alignment": "垂直对齐"
57
58
  }
package/i18n/zh-hant.json CHANGED
@@ -51,7 +51,8 @@
51
51
  "unexpected template argument": "未預期的模板參數",
52
52
  "unmatched closing tag": "未匹配的閉合標籤",
53
53
  "unnecessary URL encoding in an internal link": "內部連結中不必要的URL編碼",
54
- "useless fragment": "多餘的fragment",
54
+ "useless fragment": "無用的fragment",
55
+ "useless link text": "無用的連結文字",
55
56
  "variable anchor in a section header": "段落標題中可變的錨點",
56
57
  "vertical-alignment": "垂直對齊"
57
58
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikilint",
3
- "version": "2.6.2",
3
+ "version": "2.7.0",
4
4
  "description": "A Node.js linter for MediaWiki markup",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -32,13 +32,14 @@
32
32
  "url": "git+https://github.com/bhsd-harry/wikiparser-node.git"
33
33
  },
34
34
  "scripts": {
35
- "declaration": "grep -rl --include='*.d.ts' '@private' dist/ | xargs bash sed.sh -i -E '/^\\s+\\/\\*\\* @private/,+1d'; node ./dist/bin/declaration.js",
36
- "prepublishOnly": "npm run build && rm dist/internal.js dist/[bpu]*/*.d.ts",
37
- "build": "bash build.sh",
35
+ "declaration": "grep -rl --include='*.d.ts' '@private' dist/ | xargs bash sed.sh -i -E '/^\\s+\\/\\*\\* @private/,+1d'; grep -rl --include='*.d.ts' '/util/' dist/ | xargs bash sed.sh -i -E '/^import .+\\/util\\//d'; node ./dist/bin/declaration.js",
36
+ "prepublishOnly": "npm run build",
37
+ "build:core": "bash build.sh",
38
+ "build": "npm run build:core",
38
39
  "diff": "bash diff.sh",
39
40
  "diff:stat": "f() { git diff --stat --ignore-all-space --color=always $1 $2 -- . ':!extensions/' ':!bin/' | grep '\\.ts'; }; f",
40
41
  "lint:ts": "tsc --noEmit && eslint --cache .",
41
- "lint:json": "ajv -s config/.schema.json -d 'config/*.json' --strict=true --strict-required=false",
42
+ "lint:json": "v8r -s config/.schema.json config/*.json",
42
43
  "lint": "npm run lint:ts && npm run lint:json",
43
44
  "test": "node dist/test/test.js",
44
45
  "test:end": "pkill -x http-server",
@@ -54,17 +55,17 @@
54
55
  "@types/request": "^2.48.12",
55
56
  "@typescript-eslint/eslint-plugin": "^7.1.0",
56
57
  "@typescript-eslint/parser": "^7.1.0",
57
- "ajv-cli": "^5.0.0",
58
58
  "eslint": "^8.56.0",
59
59
  "eslint-plugin-es-x": "^7.5.0",
60
60
  "eslint-plugin-eslint-comments": "^3.2.0",
61
61
  "eslint-plugin-jsdoc": "^48.0.2",
62
62
  "eslint-plugin-json-es": "^1.5.7",
63
- "eslint-plugin-n": "^16.6.2",
63
+ "eslint-plugin-n": "^17.4.0",
64
64
  "eslint-plugin-promise": "^6.1.1",
65
65
  "eslint-plugin-regexp": "^2.2.0",
66
- "eslint-plugin-unicorn": "^51.0.1",
67
- "typescript": "^5.3.3"
66
+ "eslint-plugin-unicorn": "^52.0.0",
67
+ "typescript": "^5.3.3",
68
+ "v8r": "^3.0.0"
68
69
  },
69
70
  "engines": {
70
71
  "node": "^20.9.0"