wikilint 2.5.1 → 2.5.3
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/config/default.json +1 -2
- package/config/enwiki.json +1 -2
- package/dist/lib/text.js +11 -1
- package/dist/lib/title.d.ts +6 -0
- package/dist/lib/title.js +15 -0
- package/dist/parser/list.js +27 -9
- package/dist/src/attribute.d.ts +2 -0
- package/dist/src/attribute.js +1 -1
- package/dist/src/imageParameter.d.ts +1 -1
- package/dist/src/imageParameter.js +7 -4
- package/dist/src/link/base.js +2 -2
- package/dist/src/link/file.d.ts +2 -0
- package/dist/src/link/file.js +6 -1
- package/package.json +7 -5
package/config/default.json
CHANGED
package/config/enwiki.json
CHANGED
package/dist/lib/text.js
CHANGED
|
@@ -83,8 +83,18 @@ class AstText extends node_1.AstNode {
|
|
|
83
83
|
if (!parentNode) {
|
|
84
84
|
return [];
|
|
85
85
|
}
|
|
86
|
+
const { type, name, parentNode: grandparent } = parentNode, nowiki = name === 'nowiki' || name === 'pre';
|
|
87
|
+
let isHtmlAttrVal = false;
|
|
88
|
+
if (type === 'attr-value') {
|
|
89
|
+
const { type: grandType, name: grandName, tag } = grandparent;
|
|
90
|
+
if (grandType !== 'ext-attr') {
|
|
91
|
+
isHtmlAttrVal = true;
|
|
92
|
+
}
|
|
93
|
+
else if (tag === 'choose' && (grandName === 'before' || grandName === 'after')) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
86
97
|
const { NowikiToken } = require('../src/nowiki');
|
|
87
|
-
const { type, name } = parentNode, nowiki = name === 'nowiki' || name === 'pre', isHtmlAttrVal = type === 'attr-value' && parentNode.parentNode.type !== 'ext-attr';
|
|
88
98
|
let errorRegex;
|
|
89
99
|
if (type === 'ext-inner' && (name === 'pre' || parentNode instanceof NowikiToken)) {
|
|
90
100
|
errorRegex = new RegExp(`<\\s*(?:\\/\\s*)${nowiki ? '' : '?'}(${name})\\b`, 'giu');
|
package/dist/lib/title.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import Parser from '../index';
|
|
2
2
|
/** MediaWiki页面标题对象 */
|
|
3
3
|
export declare class Title {
|
|
4
|
+
#private;
|
|
4
5
|
readonly valid: boolean;
|
|
5
6
|
ns: number;
|
|
6
7
|
fragment: string | undefined;
|
|
8
|
+
/** 不含命名空间的标题主体部分 */
|
|
9
|
+
get main(): string;
|
|
10
|
+
set main(title: string);
|
|
11
|
+
/** 扩展名 */
|
|
12
|
+
get extension(): string | undefined;
|
|
7
13
|
/**
|
|
8
14
|
* @param title 标题(含或不含命名空间前缀)
|
|
9
15
|
* @param defaultNs 命名空间
|
package/dist/lib/title.js
CHANGED
|
@@ -10,6 +10,20 @@ class Title {
|
|
|
10
10
|
fragment;
|
|
11
11
|
/** @private */
|
|
12
12
|
encoded = false;
|
|
13
|
+
#main;
|
|
14
|
+
/** 不含命名空间的标题主体部分 */
|
|
15
|
+
get main() {
|
|
16
|
+
return this.#main;
|
|
17
|
+
}
|
|
18
|
+
set main(title) {
|
|
19
|
+
title = title.replace(/_/gu, ' ').trim();
|
|
20
|
+
this.#main = title && `${title[0].toUpperCase()}${title.slice(1)}`;
|
|
21
|
+
}
|
|
22
|
+
/** 扩展名 */
|
|
23
|
+
get extension() {
|
|
24
|
+
const { main } = this, i = main.lastIndexOf('.');
|
|
25
|
+
return i === -1 ? undefined : main.slice(i + 1).toLowerCase();
|
|
26
|
+
}
|
|
13
27
|
/**
|
|
14
28
|
* @param title 标题(含或不含命名空间前缀)
|
|
15
29
|
* @param defaultNs 命名空间
|
|
@@ -61,6 +75,7 @@ class Title {
|
|
|
61
75
|
}
|
|
62
76
|
this.valid = Boolean(title || this.interwiki || selfLink && this.fragment !== undefined)
|
|
63
77
|
&& !/^:|\0\d+[eh!+-]\x7F|[<>[\]{}|]|%[\da-f]{2}/iu.test(title);
|
|
78
|
+
this.main = title;
|
|
64
79
|
}
|
|
65
80
|
}
|
|
66
81
|
exports.Title = Title;
|
package/dist/parser/list.js
CHANGED
|
@@ -22,8 +22,8 @@ const parseList = (wikitext, config = index_1.default.getConfig(), accum = []) =
|
|
|
22
22
|
if (!dt) {
|
|
23
23
|
return text;
|
|
24
24
|
}
|
|
25
|
-
const { html: [normalTags] } = config, fullRegex = /:+|-\{|\0\d+
|
|
26
|
-
let regex = fullRegex, ex = regex.exec(text), lt = 0, lc = 0;
|
|
25
|
+
const { html: [normalTags] } = config, fullRegex = /:+|-\{|\0\d+[xq]\x7F/gu;
|
|
26
|
+
let regex = fullRegex, ex = regex.exec(text), lt = 0, lb = false, li = false, lc = 0;
|
|
27
27
|
/**
|
|
28
28
|
* 创建`DdToken`
|
|
29
29
|
* @param syntax `:`
|
|
@@ -34,6 +34,18 @@ const parseList = (wikitext, config = index_1.default.getConfig(), accum = []) =
|
|
|
34
34
|
new dd_1.DdToken(syntax, config, accum);
|
|
35
35
|
return `${text.slice(0, index)}\0${accum.length - 1}d\x7F${text.slice(index + syntax.length)}`;
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* 更新 `lt`
|
|
39
|
+
* @param closing 是否是闭合标签
|
|
40
|
+
*/
|
|
41
|
+
const update = (closing) => {
|
|
42
|
+
if (!closing) {
|
|
43
|
+
lt++;
|
|
44
|
+
}
|
|
45
|
+
else if (lt) {
|
|
46
|
+
lt--;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
37
49
|
while (ex && dt) {
|
|
38
50
|
const { 0: syntax, index } = ex;
|
|
39
51
|
if (syntax === '-{') {
|
|
@@ -52,15 +64,21 @@ const parseList = (wikitext, config = index_1.default.getConfig(), accum = []) =
|
|
|
52
64
|
regex.lastIndex = lastIndex;
|
|
53
65
|
}
|
|
54
66
|
}
|
|
55
|
-
else if (syntax.
|
|
67
|
+
else if (syntax.endsWith('x\x7F')) {
|
|
56
68
|
const { name, closing, selfClosing } = accum[Number(syntax.slice(1, -2))];
|
|
57
69
|
if (!selfClosing || normalTags.includes(name)) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
update(closing);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else if (syntax.endsWith('q\x7F')) {
|
|
74
|
+
const { bold, italic } = accum[Number(syntax.slice(1, -2))];
|
|
75
|
+
if (bold) {
|
|
76
|
+
update(lb);
|
|
77
|
+
lb = !lb;
|
|
78
|
+
}
|
|
79
|
+
if (italic) {
|
|
80
|
+
update(li);
|
|
81
|
+
li = !li;
|
|
64
82
|
}
|
|
65
83
|
}
|
|
66
84
|
else if (lt === 0) { // syntax === ':'
|
package/dist/src/attribute.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export declare abstract class AttributeToken extends Token {
|
|
|
18
18
|
abstract get parentNode(): AttributesToken | undefined;
|
|
19
19
|
abstract get nextSibling(): AtomToken | this | undefined;
|
|
20
20
|
abstract get previousSibling(): AtomToken | this | undefined;
|
|
21
|
+
/** 标签名 */
|
|
22
|
+
get tag(): string;
|
|
21
23
|
/** 引号是否匹配 */
|
|
22
24
|
get balanced(): boolean;
|
|
23
25
|
/**
|
package/dist/src/attribute.js
CHANGED
|
@@ -15,7 +15,7 @@ export declare abstract class ImageParameterToken extends Token {
|
|
|
15
15
|
/** 图片链接 */
|
|
16
16
|
get link(): string | Title | undefined;
|
|
17
17
|
/** @param str 图片参数 */
|
|
18
|
-
constructor(str: string, config?: Parser.Config, accum?: Token[]);
|
|
18
|
+
constructor(str: string, extension: string | undefined, config?: Parser.Config, accum?: Token[]);
|
|
19
19
|
/** @override */
|
|
20
20
|
text(): string;
|
|
21
21
|
/** @override */
|
|
@@ -6,7 +6,7 @@ const lint_1 = require("../util/lint");
|
|
|
6
6
|
const index_1 = require("../index");
|
|
7
7
|
const index_2 = require("./index");
|
|
8
8
|
exports.galleryParams = new Set(['alt', 'link', 'lang', 'page', 'caption']);
|
|
9
|
-
function validate(key, val, config
|
|
9
|
+
function validate(key, val, config, halfParsed = false, ext) {
|
|
10
10
|
val = val.trim();
|
|
11
11
|
let value = val.replace(/\0\d+t\x7F/gu, '').trim();
|
|
12
12
|
switch (key) {
|
|
@@ -29,11 +29,13 @@ function validate(key, val, config = index_1.default.getConfig(), halfParsed = f
|
|
|
29
29
|
return title.valid && title;
|
|
30
30
|
}
|
|
31
31
|
case 'lang':
|
|
32
|
-
return
|
|
32
|
+
return (ext === 'svg' || ext === 'svgz') && !/[^a-z\d-]/u.test(value);
|
|
33
33
|
case 'alt':
|
|
34
34
|
case 'class':
|
|
35
35
|
case 'manualthumb':
|
|
36
36
|
return true;
|
|
37
|
+
case 'page':
|
|
38
|
+
return (ext === 'djvu' || ext === 'djv') && Number(value) > 0;
|
|
37
39
|
default:
|
|
38
40
|
return !Number.isNaN(Number(value));
|
|
39
41
|
}
|
|
@@ -47,7 +49,7 @@ class ImageParameterToken extends index_2.Token {
|
|
|
47
49
|
return this.name === 'link' ? validate('link', super.text(), this.getAttribute('config')) : undefined;
|
|
48
50
|
}
|
|
49
51
|
/** @param str 图片参数 */
|
|
50
|
-
constructor(str, config = index_1.default.getConfig(), accum = []) {
|
|
52
|
+
constructor(str, extension, config = index_1.default.getConfig(), accum = []) {
|
|
51
53
|
let mt;
|
|
52
54
|
const regexes = Object.entries(config.img).map(([syntax, param]) => [
|
|
53
55
|
syntax,
|
|
@@ -56,7 +58,8 @@ class ImageParameterToken extends index_2.Token {
|
|
|
56
58
|
]), param = regexes.find(([, key, regex]) => {
|
|
57
59
|
mt = regex.exec(str);
|
|
58
60
|
return mt
|
|
59
|
-
&& (mt.length !== 4
|
|
61
|
+
&& (mt.length !== 4
|
|
62
|
+
|| validate(key, mt[2], config, true, extension) !== false);
|
|
60
63
|
});
|
|
61
64
|
// @ts-expect-error mt already assigned
|
|
62
65
|
if (param && mt) {
|
package/dist/src/link/base.js
CHANGED
|
@@ -114,8 +114,8 @@ class LinkBaseToken extends index_2.Token {
|
|
|
114
114
|
return errors;
|
|
115
115
|
}
|
|
116
116
|
/** @private */
|
|
117
|
-
getTitle() {
|
|
118
|
-
return this.normalizeTitle(this.firstChild.text(), 0,
|
|
117
|
+
getTitle(halfParsed = false) {
|
|
118
|
+
return this.normalizeTitle(this.firstChild.text(), 0, halfParsed, true, true);
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
exports.LinkBaseToken = LinkBaseToken;
|
package/dist/src/link/file.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export declare abstract class FileToken extends LinkBaseToken {
|
|
|
12
12
|
readonly type: 'file' | 'gallery-image' | 'imagemap-image';
|
|
13
13
|
readonly childNodes: readonly [AtomToken, ...ImageParameterToken[]];
|
|
14
14
|
abstract get lastChild(): AtomToken | ImageParameterToken;
|
|
15
|
+
/** 扩展名 */
|
|
16
|
+
get extension(): string | undefined;
|
|
15
17
|
/**
|
|
16
18
|
* @param link 文件名
|
|
17
19
|
* @param text 图片参数
|
package/dist/src/link/file.js
CHANGED
|
@@ -40,6 +40,10 @@ const explode = (start, end, separator, str) => {
|
|
|
40
40
|
*/
|
|
41
41
|
class FileToken extends base_1.LinkBaseToken {
|
|
42
42
|
type = 'file';
|
|
43
|
+
/** 扩展名 */
|
|
44
|
+
get extension() {
|
|
45
|
+
return this.getTitle().extension;
|
|
46
|
+
}
|
|
43
47
|
/**
|
|
44
48
|
* @param link 文件名
|
|
45
49
|
* @param text 图片参数
|
|
@@ -47,9 +51,10 @@ class FileToken extends base_1.LinkBaseToken {
|
|
|
47
51
|
*/
|
|
48
52
|
constructor(link, text, config = index_1.default.getConfig(), accum = [], delimiter = '|') {
|
|
49
53
|
super(link, undefined, config, accum, delimiter);
|
|
54
|
+
const { extension } = this.getTitle(true);
|
|
50
55
|
this.append(...explode('-{', '}-', '|', text).map(
|
|
51
56
|
// @ts-expect-error abstract class
|
|
52
|
-
part => new imageParameter_1.ImageParameterToken(part, config, accum)));
|
|
57
|
+
part => new imageParameter_1.ImageParameterToken(part, extension, config, accum)));
|
|
53
58
|
}
|
|
54
59
|
/** @override */
|
|
55
60
|
lint(start = this.getAbsoluteIndex()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wikilint",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.3",
|
|
4
4
|
"description": "A Node.js linter for MediaWiki markup",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mediawiki",
|
|
@@ -43,15 +43,17 @@
|
|
|
43
43
|
"test:end": "pkill -x http-server",
|
|
44
44
|
"test:real": "node dist/test/real.js"
|
|
45
45
|
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"chalk": "^4.1.2"
|
|
48
|
+
},
|
|
46
49
|
"devDependencies": {
|
|
47
50
|
"@cypress/request": "^3.0.1",
|
|
48
51
|
"@stylistic/eslint-plugin": "^1.5.4",
|
|
49
52
|
"@types/node": "^20.11.6",
|
|
50
53
|
"@types/request": "^2.48.12",
|
|
51
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
52
|
-
"@typescript-eslint/parser": "^
|
|
54
|
+
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
|
55
|
+
"@typescript-eslint/parser": "^7.1.0",
|
|
53
56
|
"ajv-cli": "^5.0.0",
|
|
54
|
-
"chalk": "^4.1.2",
|
|
55
57
|
"eslint": "^8.56.0",
|
|
56
58
|
"eslint-plugin-es-x": "^7.5.0",
|
|
57
59
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
@@ -60,7 +62,7 @@
|
|
|
60
62
|
"eslint-plugin-n": "^16.6.2",
|
|
61
63
|
"eslint-plugin-promise": "^6.1.1",
|
|
62
64
|
"eslint-plugin-regexp": "^2.2.0",
|
|
63
|
-
"eslint-plugin-unicorn": "^
|
|
65
|
+
"eslint-plugin-unicorn": "^51.0.1",
|
|
64
66
|
"typescript": "^5.3.3"
|
|
65
67
|
},
|
|
66
68
|
"engines": {
|