wikilint 2.6.1 → 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.
- package/config/.schema.json +9 -0
- package/config/default.json +5 -0
- package/config/enwiki.json +3 -0
- package/config/llwiki.json +4 -0
- package/config/minimum.json +3 -0
- package/config/moegirl.json +4 -0
- package/config/zhwiki.json +5 -0
- package/dist/base.d.ts +4 -5
- package/dist/bin/cli.js +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +10 -4
- package/dist/internal.d.ts +3 -1
- package/dist/lib/element.d.ts +10 -0
- package/dist/lib/element.js +60 -1
- package/dist/lib/node.d.ts +1 -2
- package/dist/lib/node.js +19 -18
- package/dist/lib/text.d.ts +1 -0
- package/dist/lib/text.js +7 -4
- package/dist/lib/title.d.ts +10 -3
- package/dist/lib/title.js +56 -20
- package/dist/mixin/attributesParent.d.ts +0 -1
- package/dist/mixin/hidden.d.ts +0 -1
- package/dist/parser/braces.js +1 -2
- package/dist/parser/commentAndExt.js +2 -8
- package/dist/parser/converter.js +1 -4
- package/dist/parser/externalLinks.js +17 -7
- package/dist/parser/hrAndDoubleUnderscore.js +1 -2
- package/dist/parser/html.js +7 -8
- package/dist/parser/links.js +9 -5
- package/dist/parser/list.js +1 -2
- package/dist/parser/magicLinks.js +4 -7
- package/dist/parser/quotes.js +1 -2
- package/dist/parser/redirect.js +22 -0
- package/dist/parser/table.js +6 -7
- package/dist/src/attribute.js +2 -2
- package/dist/src/attributes.js +0 -2
- package/dist/src/extLink.js +5 -2
- package/dist/src/heading.js +2 -2
- package/dist/src/html.js +1 -1
- package/dist/src/imageParameter.js +3 -5
- package/dist/src/imagemap.js +1 -1
- package/dist/src/index.d.ts +1 -3
- package/dist/src/index.js +38 -26
- package/dist/src/link/base.d.ts +1 -1
- package/dist/src/link/base.js +3 -3
- package/dist/src/link/category.d.ts +6 -1
- package/dist/src/link/category.js +4 -1
- package/dist/src/link/galleryImage.js +2 -1
- package/dist/src/link/redirectTarget.d.ts +25 -0
- package/dist/src/link/redirectTarget.js +50 -0
- package/dist/src/magicLink.js +1 -1
- package/dist/src/paramTag/inputbox.js +2 -2
- package/dist/src/parameter.js +0 -2
- package/dist/src/pre.js +1 -1
- package/dist/src/redirect.d.ts +26 -0
- package/dist/src/redirect.js +46 -0
- package/dist/src/syntax.d.ts +2 -3
- package/dist/src/table/index.js +7 -4
- package/dist/src/table/td.js +2 -2
- package/dist/src/table/trBase.js +1 -1
- package/dist/src/transclude.js +5 -5
- package/dist/util/debug.js +11 -2
- package/dist/util/diff.js +2 -2
- package/dist/util/string.js +4 -1
- package/i18n/zh-hans.json +2 -1
- package/i18n/zh-hant.json +2 -1
- package/package.json +11 -9
package/config/.schema.json
CHANGED
|
@@ -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
|
package/config/default.json
CHANGED
package/config/enwiki.json
CHANGED
package/config/llwiki.json
CHANGED
package/config/minimum.json
CHANGED
|
@@ -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
|
}
|
package/config/moegirl.json
CHANGED
package/config/zhwiki.json
CHANGED
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,11 +47,8 @@ export interface AstNode {
|
|
|
45
47
|
interface AstElement extends AstNode {
|
|
46
48
|
}
|
|
47
49
|
export interface Parser {
|
|
48
|
-
config:
|
|
49
|
-
i18n:
|
|
50
|
-
rules: readonly LintError.Rule[];
|
|
51
|
-
/** 获取解析设置 */
|
|
52
|
-
getConfig(): Config;
|
|
50
|
+
config: Config | string;
|
|
51
|
+
i18n: Record<string, string> | string | undefined;
|
|
53
52
|
/**
|
|
54
53
|
* 解析wikitext
|
|
55
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 =
|
|
121
|
+
wikitext = wikitext.slice(0, from) + text + wikitext.slice(to);
|
|
122
122
|
start = from;
|
|
123
123
|
}
|
|
124
124
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Config, LintError, Parser as ParserBase } from './base';
|
|
|
2
2
|
import type { Title } from './lib/title';
|
|
3
3
|
import type { Token } from './internal';
|
|
4
4
|
declare interface Parser extends ParserBase {
|
|
5
|
+
rules: readonly LintError.Rule[];
|
|
5
6
|
/**
|
|
6
7
|
* 规范化页面标题
|
|
7
8
|
* @param title 标题(含或不含命名空间前缀)
|
|
@@ -18,3 +19,6 @@ declare const Parser: Parser;
|
|
|
18
19
|
export = Parser;
|
|
19
20
|
export type { Config, LintError };
|
|
20
21
|
export type * from './internal';
|
|
22
|
+
declare global {
|
|
23
|
+
type Acceptable = unknown;
|
|
24
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -37,13 +37,21 @@ const Parser = {
|
|
|
37
37
|
return msg && (this.i18n?.[msg] ?? msg).replace('$1', this.msg(arg));
|
|
38
38
|
},
|
|
39
39
|
/** @implements */
|
|
40
|
-
normalizeTitle(title, defaultNs = 0, include
|
|
40
|
+
normalizeTitle(title, defaultNs = 0, include, config = Parser.getConfig(), halfParsed, decode = false, selfLink = false) {
|
|
41
41
|
const { Title } = require('./lib/title');
|
|
42
42
|
if (halfParsed) {
|
|
43
43
|
return new Title(title, defaultNs, config, decode, selfLink);
|
|
44
44
|
}
|
|
45
45
|
const { Token } = require('./src/index');
|
|
46
46
|
const token = debug_1.Shadow.run(() => new Token(title, config).parseOnce(0, include).parseOnce()), titleObj = new Title(String(token), defaultNs, config, decode, selfLink);
|
|
47
|
+
debug_1.Shadow.run(() => {
|
|
48
|
+
for (const key of ['main', 'fragment']) {
|
|
49
|
+
const str = titleObj[key];
|
|
50
|
+
if (str?.includes('\0')) {
|
|
51
|
+
titleObj[key] = token.buildFromStr(str, constants_1.BuildMethod.Text);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
47
55
|
return titleObj;
|
|
48
56
|
},
|
|
49
57
|
/** @implements */
|
|
@@ -60,9 +68,7 @@ const Parser = {
|
|
|
60
68
|
const file = path.join(__dirname, '..', 'errors', new Date().toISOString()), stage = token.getAttribute('stage');
|
|
61
69
|
fs.writeFileSync(file, stage === constants_1.MAX_STAGE ? wikitext : String(token));
|
|
62
70
|
fs.writeFileSync(`${file}.err`, e.stack);
|
|
63
|
-
fs.writeFileSync(`${file}.json`, JSON.stringify({
|
|
64
|
-
stage, include: token.getAttribute('include'), config: this.config,
|
|
65
|
-
}, null, '\t'));
|
|
71
|
+
fs.writeFileSync(`${file}.json`, JSON.stringify({ stage, include, config }, null, '\t'));
|
|
66
72
|
}
|
|
67
73
|
throw e;
|
|
68
74
|
}
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export type { AstNodes, } from './lib/node';
|
|
2
2
|
export type * from './lib/text';
|
|
3
|
-
export type
|
|
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';
|
package/dist/lib/element.d.ts
CHANGED
|
@@ -31,6 +31,16 @@ 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;
|
|
39
|
+
/**
|
|
40
|
+
* 符合选择器的所有后代节点
|
|
41
|
+
* @param selector 选择器
|
|
42
|
+
*/
|
|
43
|
+
querySelectorAll<T = Token>(selector: string): T[];
|
|
34
44
|
/**
|
|
35
45
|
* 在末尾批量插入子节点
|
|
36
46
|
* @param elements 插入节点
|
package/dist/lib/element.js
CHANGED
|
@@ -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
|
-
|
|
36
|
+
remove(i);
|
|
30
37
|
}
|
|
31
38
|
}
|
|
32
39
|
this.setAttribute('childNodes', childNodes);
|
|
@@ -70,6 +77,58 @@ 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
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* 符合条件的所有后代节点
|
|
109
|
+
* @param condition 条件
|
|
110
|
+
*/
|
|
111
|
+
#getElementsBy(condition) {
|
|
112
|
+
const descendants = [];
|
|
113
|
+
for (const child of this.childNodes) {
|
|
114
|
+
if (child.type === 'text') {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
else if (condition(child)) {
|
|
118
|
+
descendants.push(child);
|
|
119
|
+
}
|
|
120
|
+
descendants.push(...child.#getElementsBy(condition));
|
|
121
|
+
}
|
|
122
|
+
return descendants;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 符合选择器的所有后代节点
|
|
126
|
+
* @param selector 选择器
|
|
127
|
+
*/
|
|
128
|
+
querySelectorAll(selector) {
|
|
129
|
+
const condition = this.#getCondition(selector);
|
|
130
|
+
return this.#getElementsBy(condition);
|
|
131
|
+
}
|
|
73
132
|
/**
|
|
74
133
|
* 在末尾批量插入子节点
|
|
75
134
|
* @param elements 插入节点
|
package/dist/lib/node.d.ts
CHANGED
|
@@ -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
|
|
70
|
-
return { top, left:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
/** 获取当前节点的绝对位置 */
|
package/dist/lib/text.d.ts
CHANGED
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
|
-
|
|
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.d.ts
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { Config } from '../base';
|
|
2
2
|
/** MediaWiki页面标题对象 */
|
|
3
3
|
export declare class Title {
|
|
4
4
|
#private;
|
|
5
|
-
readonly valid: boolean;
|
|
6
5
|
ns: number;
|
|
7
6
|
fragment: string | undefined;
|
|
7
|
+
interwiki: string;
|
|
8
|
+
readonly valid: boolean;
|
|
8
9
|
/** 不含命名空间的标题主体部分 */
|
|
9
10
|
get main(): string;
|
|
10
11
|
set main(title: string);
|
|
12
|
+
/** 命名空间前缀 */
|
|
13
|
+
get prefix(): string;
|
|
14
|
+
/** 完整标题 */
|
|
15
|
+
get title(): string;
|
|
11
16
|
/** 扩展名 */
|
|
12
17
|
get extension(): string | undefined;
|
|
13
18
|
/**
|
|
19
|
+
* @see MediaWikiTitleCodec::splitTitleString
|
|
20
|
+
*
|
|
14
21
|
* @param title 标题(含或不含命名空间前缀)
|
|
15
22
|
* @param defaultNs 命名空间
|
|
16
23
|
* @param decode 是否需要解码
|
|
17
24
|
* @param selfLink 是否允许selfLink
|
|
18
25
|
*/
|
|
19
|
-
constructor(title: string, defaultNs
|
|
26
|
+
constructor(title: string, defaultNs: number, config: Config, decode: boolean, selfLink: boolean);
|
|
20
27
|
}
|
package/dist/lib/title.js
CHANGED
|
@@ -2,22 +2,40 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Title = void 0;
|
|
4
4
|
const string_1 = require("../util/string");
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* PHP的`rawurldecode`函数的JavaScript实现
|
|
7
|
+
* @param str 要解码的字符串
|
|
8
|
+
*/
|
|
9
|
+
const rawurldecode = (str) => decodeURIComponent(str.replace(/%(?![\da-f]{2})/giu, '%25'));
|
|
6
10
|
/** MediaWiki页面标题对象 */
|
|
7
11
|
class Title {
|
|
8
|
-
|
|
12
|
+
#main;
|
|
13
|
+
#namespaces;
|
|
9
14
|
ns;
|
|
10
15
|
fragment;
|
|
16
|
+
interwiki = '';
|
|
17
|
+
valid;
|
|
11
18
|
/** @private */
|
|
12
19
|
encoded = false;
|
|
13
|
-
#main;
|
|
14
20
|
/** 不含命名空间的标题主体部分 */
|
|
15
21
|
get main() {
|
|
16
22
|
return this.#main;
|
|
17
23
|
}
|
|
18
24
|
set main(title) {
|
|
19
25
|
title = title.replace(/_/gu, ' ').trim();
|
|
20
|
-
this.#main = title &&
|
|
26
|
+
this.#main = title && title[0].toUpperCase() + title.slice(1);
|
|
27
|
+
}
|
|
28
|
+
/** 命名空间前缀 */
|
|
29
|
+
get prefix() {
|
|
30
|
+
const namespace = this.#namespaces[this.ns];
|
|
31
|
+
return namespace + (namespace && ':');
|
|
32
|
+
}
|
|
33
|
+
/** 完整标题 */
|
|
34
|
+
get title() {
|
|
35
|
+
const prefix = this.interwiki + (this.interwiki && ':') + this.prefix;
|
|
36
|
+
// eslint-disable-next-line prefer-const
|
|
37
|
+
let title = (prefix + this.main).replace(/ /gu, '_');
|
|
38
|
+
return title;
|
|
21
39
|
}
|
|
22
40
|
/** 扩展名 */
|
|
23
41
|
get extension() {
|
|
@@ -25,51 +43,69 @@ class Title {
|
|
|
25
43
|
return i === -1 ? undefined : main.slice(i + 1).toLowerCase();
|
|
26
44
|
}
|
|
27
45
|
/**
|
|
46
|
+
* @see MediaWikiTitleCodec::splitTitleString
|
|
47
|
+
*
|
|
28
48
|
* @param title 标题(含或不含命名空间前缀)
|
|
29
49
|
* @param defaultNs 命名空间
|
|
30
50
|
* @param decode 是否需要解码
|
|
31
51
|
* @param selfLink 是否允许selfLink
|
|
32
52
|
*/
|
|
33
|
-
constructor(title, defaultNs
|
|
53
|
+
constructor(title, defaultNs, config, decode, selfLink) {
|
|
54
|
+
const subpage = title.trim().startsWith('../');
|
|
34
55
|
title = (0, string_1.decodeHtml)(title);
|
|
35
56
|
if (decode && title.includes('%')) {
|
|
36
57
|
try {
|
|
37
58
|
const encoded = /%(?!21|3[ce]|5[bd]|7[b-d])[\da-f]{2}/iu.test(title);
|
|
38
|
-
title =
|
|
59
|
+
title = rawurldecode(title);
|
|
39
60
|
this.encoded = encoded;
|
|
40
61
|
}
|
|
41
62
|
catch { }
|
|
42
63
|
}
|
|
43
64
|
title = title.replace(/_/gu, ' ').trim();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
ns = 0;
|
|
47
|
-
title = title.slice(1).trim();
|
|
65
|
+
if (subpage) {
|
|
66
|
+
this.ns = 0;
|
|
48
67
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
else {
|
|
69
|
+
let ns = defaultNs;
|
|
70
|
+
if (title.startsWith(':')) {
|
|
71
|
+
ns = 0;
|
|
72
|
+
title = title.slice(1).trim();
|
|
73
|
+
}
|
|
74
|
+
const m = title.split(':');
|
|
75
|
+
if (m.length > 1) {
|
|
76
|
+
const id = config.nsid[m[0].trim().toLowerCase()];
|
|
77
|
+
if (id) {
|
|
78
|
+
ns = id;
|
|
79
|
+
title = m.slice(1).join(':').trim();
|
|
80
|
+
}
|
|
55
81
|
}
|
|
82
|
+
this.ns = ns;
|
|
56
83
|
}
|
|
57
|
-
this.ns = ns;
|
|
58
84
|
const i = title.indexOf('#');
|
|
59
85
|
if (i !== -1) {
|
|
60
86
|
let fragment = title.slice(i + 1).trimEnd();
|
|
61
87
|
if (fragment.includes('%')) {
|
|
62
88
|
try {
|
|
63
|
-
fragment =
|
|
89
|
+
fragment = rawurldecode(fragment);
|
|
64
90
|
}
|
|
65
91
|
catch { }
|
|
66
92
|
}
|
|
67
93
|
this.fragment = fragment;
|
|
68
94
|
title = title.slice(0, i).trim();
|
|
69
95
|
}
|
|
70
|
-
this.valid = Boolean(title || this.interwiki || selfLink && this.fragment !== undefined)
|
|
71
|
-
&& !/^:|\0\d+[eh!+-]\x7F|[<>[\]{}
|
|
96
|
+
this.valid = Boolean(title || this.interwiki || selfLink && this.ns === 0 && this.fragment !== undefined)
|
|
97
|
+
&& !/^:|\0\d+[eh!+-]\x7F|[<>[\]{}|\n]|%[\da-f]{2}|(?:^|\/)\.{1,2}(?:$|\/)/iu.test(subpage ? /^(?:\.\.\/)+(.*)/u.exec(title)[1] : title);
|
|
72
98
|
this.main = title;
|
|
99
|
+
Object.defineProperties(this, {
|
|
100
|
+
encoded: { enumerable: false, writable: false },
|
|
101
|
+
});
|
|
102
|
+
this.#namespaces = config.namespaces;
|
|
103
|
+
}
|
|
104
|
+
/** @private */
|
|
105
|
+
toString() {
|
|
106
|
+
return `${this.title}${this.fragment === undefined
|
|
107
|
+
? ''
|
|
108
|
+
: `#${this.fragment}`}`;
|
|
73
109
|
}
|
|
74
110
|
}
|
|
75
111
|
exports.Title = Title;
|
package/dist/mixin/hidden.d.ts
CHANGED
package/dist/parser/braces.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseBraces = void 0;
|
|
4
4
|
const string_1 = require("../util/string");
|
|
5
|
-
const index_1 = require("../index");
|
|
6
5
|
const heading_1 = require("../src/heading");
|
|
7
6
|
const transclude_1 = require("../src/transclude");
|
|
8
7
|
const arg_1 = require("../src/arg");
|
|
@@ -13,7 +12,7 @@ const arg_1 = require("../src/arg");
|
|
|
13
12
|
* @param accum
|
|
14
13
|
* @throws TranscludeToken.constructor()
|
|
15
14
|
*/
|
|
16
|
-
const parseBraces = (wikitext, config
|
|
15
|
+
const parseBraces = (wikitext, config, accum) => {
|
|
17
16
|
const source = `${config.excludes?.includes('heading') ? '' : '^(\0\\d+c\x7F)*={1,6}|'}\\[\\[|\\{{2,}|-\\{(?!\\{)`, { parserFunction: [, , , subst] } = config, stack = [], closes = { '=': '\n', '{': '\\}{2,}|\\|', '-': '\\}-', '[': '\\]\\]' }, marks = new Map([['!', '!'], ['!!', '+'], ['(!', '{'], ['!)', '}'], ['!-', '-'], ['=', '~']]);
|
|
18
17
|
let regex = new RegExp(source, 'gmu'), mt = regex.exec(wikitext), moreBraces = wikitext.includes('}}'), lastIndex;
|
|
19
18
|
while (mt
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseCommentAndExt = void 0;
|
|
4
|
-
const index_1 = require("../index");
|
|
5
4
|
const onlyinclude_1 = require("../src/onlyinclude");
|
|
6
5
|
const noinclude_1 = require("../src/nowiki/noinclude");
|
|
7
6
|
const include_1 = require("../src/tagPair/include");
|
|
@@ -14,7 +13,7 @@ const comment_1 = require("../src/nowiki/comment");
|
|
|
14
13
|
* @param accum
|
|
15
14
|
* @param includeOnly 是否嵌入
|
|
16
15
|
*/
|
|
17
|
-
const parseCommentAndExt = (wikitext, config
|
|
16
|
+
const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
|
|
18
17
|
const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', { length } = onlyincludeLeft;
|
|
19
18
|
/** 更新`<onlyinclude>`和`</onlyinclude>`的位置 */
|
|
20
19
|
const update = () => {
|
|
@@ -50,14 +49,9 @@ const parseCommentAndExt = (wikitext, config = index_1.default.getConfig(), accu
|
|
|
50
49
|
return str;
|
|
51
50
|
}
|
|
52
51
|
}
|
|
53
|
-
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
|
54
|
-
/<foo(?:\s[^>]*)?>|<\/foo\s*>/giu;
|
|
55
|
-
/<(bar)(\s[^>]*?)?(?:\/>|>(.*?)<\/(\1\s*)>)/gisu;
|
|
56
|
-
/<(baz)(\s[^>]*?)?(?:\/>|>(.*?)(?:<\/(baz\s*)>|$))/gisu;
|
|
57
|
-
/* eslint-enable @typescript-eslint/no-unused-expressions */
|
|
58
52
|
const ext = config.ext.join('|'), noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly', regex = new RegExp('<!--.*?(?:-->|$)' // comment
|
|
59
53
|
+ '|'
|
|
60
|
-
+ `<${noincludeRegex}(?:\\s[^>]*)
|
|
54
|
+
+ `<${noincludeRegex}(?:\\s[^>]*)?/?>|</${noincludeRegex}\\s*>` // <noinclude>
|
|
61
55
|
+ '|'
|
|
62
56
|
+ `<(${ext})(\\s[^>]*?)?(?:/>|>(.*?)</(\\1\\s*)>)` // 扩展标签
|
|
63
57
|
+ '|'
|