wikilint 2.18.2 → 2.18.4
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/README.md +1 -1
- package/bin/cli.js +1 -1
- package/config/minimum.json +7 -0
- package/dist/base.d.mts +2 -0
- package/dist/base.d.ts +2 -0
- package/dist/base.js +1 -0
- package/dist/base.mjs +2 -1
- package/dist/bin/config.js +3 -2
- package/dist/index.d.ts +12 -1
- package/dist/index.js +45 -28
- package/dist/lib/lsp.d.ts +1 -0
- package/dist/lib/lsp.js +19 -14
- package/dist/lib/text.js +2 -2
- package/dist/lib/title.d.ts +18 -4
- package/dist/lib/title.js +12 -4
- package/dist/parser/braces.js +79 -37
- package/dist/parser/commentAndExt.js +5 -16
- package/dist/parser/links.js +1 -1
- package/dist/parser/redirect.js +1 -3
- package/dist/src/arg.js +1 -1
- package/dist/src/attribute.js +1 -1
- package/dist/src/converter.js +1 -1
- package/dist/src/gallery.js +1 -1
- package/dist/src/heading.js +3 -3
- package/dist/src/imageParameter.js +9 -9
- package/dist/src/imagemap.js +3 -2
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +7 -5
- package/dist/src/link/base.js +1 -1
- package/dist/src/link/file.d.ts +2 -0
- package/dist/src/link/file.js +15 -13
- package/dist/src/link/galleryImage.js +1 -1
- package/dist/src/link/redirectTarget.js +1 -2
- package/dist/src/magicLink.js +1 -1
- package/dist/src/nested.js +5 -5
- package/dist/src/nowiki/index.js +3 -2
- package/dist/src/redirect.js +1 -2
- package/dist/src/syntax.d.ts +4 -2
- package/dist/src/syntax.js +4 -2
- package/dist/src/table/base.js +1 -1
- package/dist/src/table/index.d.ts +1 -0
- package/dist/src/table/index.js +3 -6
- package/dist/src/table/trBase.js +31 -14
- package/dist/src/transclude.js +14 -6
- package/dist/util/debug.js +11 -1
- package/dist/util/diff.js +1 -1
- package/dist/util/string.js +3 -4
- package/package.json +2 -2
package/dist/parser/braces.js
CHANGED
|
@@ -1,16 +1,40 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseBraces = void 0;
|
|
4
|
+
const common_1 = require("@bhsd/common");
|
|
4
5
|
const string_1 = require("../util/string");
|
|
5
6
|
const heading_1 = require("../src/heading");
|
|
6
7
|
const transclude_1 = require("../src/transclude");
|
|
7
8
|
const arg_1 = require("../src/arg");
|
|
9
|
+
/* NOT FOR BROWSER ONLY */
|
|
10
|
+
const v8_1 = require("v8");
|
|
11
|
+
const MAXHEAP = (0, v8_1.getHeapStatistics)().heap_size_limit * 0.9;
|
|
12
|
+
/* NOT FOR BROWSER ONLY END */
|
|
8
13
|
const closes = {
|
|
9
14
|
'=': String.raw `\n(?!(?:[^\S\n]|\0\d+[cn]\x7F)*\n)`,
|
|
10
15
|
'{': String.raw `\}{2,}|\|`,
|
|
11
16
|
'-': String.raw `\}-`,
|
|
12
17
|
'[': String.raw `\]\]`,
|
|
13
|
-
}, openBraces = String.raw `|\{{2,}`, marks = new Map([['!', '!'], ['!!', '+'], ['(!', '{'], ['!)', '}'], ['!-', '-'], ['=', '~'], ['server', 'm']]);
|
|
18
|
+
}, lbrack = String.raw `\[(?!\[)`, newline = String.raw `\n(?![=\0])`, openBraces = String.raw `|\{{2,}`, marks = new Map([['!', '!'], ['!!', '+'], ['(!', '{'], ['!)', '}'], ['!-', '-'], ['=', '~'], ['server', 'm']]), getExecRegex = (0, common_1.getRegex)(s => new RegExp(s, 'gmu'));
|
|
19
|
+
let reReplace;
|
|
20
|
+
/* NOT FOR BROWSER ONLY */
|
|
21
|
+
try {
|
|
22
|
+
reReplace = new RegExp(String.raw `(?<!\{)\{\{((?:[^\n{}[]|${lbrack}|${newline})*)\}\}` // eslint-disable-line prefer-template
|
|
23
|
+
+ '|'
|
|
24
|
+
+ String.raw `\{\{((?:[^\n{}[]|${lbrack}|${newline})*)\}\}(?!\})`
|
|
25
|
+
+ '|'
|
|
26
|
+
+ String.raw `\[\[(?:[^\n[\]{]|${newline})*\]\]`
|
|
27
|
+
+ '|'
|
|
28
|
+
+ String.raw `-\{(?:[^\n{}[]|${lbrack}|${newline})*\}-`, 'gu');
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
/* NOT FOR BROWSER ONLY END */
|
|
32
|
+
reReplace = new RegExp(String.raw `\{\{((?:[^\n{}[]|${lbrack}|${newline})*)\}\}(?!\})` // eslint-disable-line prefer-template
|
|
33
|
+
+ '|'
|
|
34
|
+
+ String.raw `\[\[(?:[^\n[\]{]|${newline})*\]\]`
|
|
35
|
+
+ '|'
|
|
36
|
+
+ String.raw `-\{(?:[^\n{}[]|${lbrack}|${newline})*\}-`, 'gu');
|
|
37
|
+
}
|
|
14
38
|
/**
|
|
15
39
|
* 获取模板或魔术字对应的字符
|
|
16
40
|
* @param s 模板或魔术字名
|
|
@@ -33,7 +57,8 @@ const getSymbol = (s) => {
|
|
|
33
57
|
* @param wikitext
|
|
34
58
|
* @param config
|
|
35
59
|
* @param accum
|
|
36
|
-
* @throws
|
|
60
|
+
* @throws `RangeError` Maximum iteration exceeded
|
|
61
|
+
* @throws `TranscludeToken.constructor()`
|
|
37
62
|
*/
|
|
38
63
|
const parseBraces = (wikitext, config, accum) => {
|
|
39
64
|
const source = String.raw `${config.excludes?.includes('heading') ? '' : String.raw `^((?:\0\d+[cno]\x7F)*)={1,6}|`}\[\[|-\{(?!\{)`, { parserFunction: [, , , subst] } = config, stack = [], linkStack = [];
|
|
@@ -42,46 +67,61 @@ const parseBraces = (wikitext, config, accum) => {
|
|
|
42
67
|
* @param s 不含内链的字符串
|
|
43
68
|
*/
|
|
44
69
|
const restore = (s) => s.replace(/\0(\d+)\x7F/gu, (_, p1) => linkStack[p1]);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
70
|
+
/**
|
|
71
|
+
* 填入模板内容
|
|
72
|
+
* @param text wikitext全文
|
|
73
|
+
* @param parts 模板参数
|
|
74
|
+
* @param lastIndex 匹配的起始位置
|
|
75
|
+
* @param index 匹配位置
|
|
76
|
+
*/
|
|
77
|
+
const push = (text, parts, lastIndex, index) => {
|
|
78
|
+
parts[parts.length - 1].push(restore(text.slice(lastIndex, index)));
|
|
79
|
+
};
|
|
80
|
+
let replaced;
|
|
81
|
+
do {
|
|
82
|
+
if (replaced !== undefined) {
|
|
83
|
+
wikitext = replaced;
|
|
84
|
+
}
|
|
85
|
+
replaced = wikitext.replace(reReplace, (m, p1, p2) => {
|
|
86
|
+
if (p1 !== undefined || typeof p2 === 'string') {
|
|
87
|
+
try {
|
|
88
|
+
const { length } = accum, parts = (p1 ?? p2).split('|');
|
|
89
|
+
// @ts-expect-error abstract class
|
|
90
|
+
new transclude_1.TranscludeToken(restore(parts[0]), parts.slice(1).map(part => {
|
|
91
|
+
const i = part.indexOf('=');
|
|
92
|
+
return (i === -1 ? [part] : [part.slice(0, i), part.slice(i + 1)]).map(restore);
|
|
93
|
+
}), config, accum);
|
|
94
|
+
return `\0${length}${getSymbol(parts[0])}\x7F`;
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
/* istanbul ignore if */
|
|
98
|
+
if (!(e instanceof SyntaxError) || e.message !== 'Invalid template name') {
|
|
99
|
+
throw e;
|
|
100
|
+
}
|
|
60
101
|
}
|
|
61
102
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
});
|
|
103
|
+
linkStack.push(restore(m));
|
|
104
|
+
return `\0${linkStack.length - 1}\x7F`;
|
|
105
|
+
});
|
|
106
|
+
} while (replaced !== wikitext);
|
|
107
|
+
wikitext = replaced;
|
|
66
108
|
const lastBraces = wikitext.lastIndexOf('}}') - wikitext.length;
|
|
67
109
|
let moreBraces = lastBraces + wikitext.length !== -1;
|
|
68
|
-
let regex =
|
|
110
|
+
let regex = getExecRegex(source + (moreBraces ? openBraces : '')), mt = regex.exec(wikitext), lastIndex;
|
|
69
111
|
while (mt
|
|
70
112
|
|| lastIndex !== undefined && lastIndex <= wikitext.length
|
|
71
113
|
&& stack[stack.length - 1]?.[0]?.startsWith('=')) {
|
|
114
|
+
/* NOT FOR BROWSER ONLY */
|
|
115
|
+
if (process.memoryUsage().heapUsed > MAXHEAP) {
|
|
116
|
+
throw new RangeError('Maximum heap size exceeded');
|
|
117
|
+
}
|
|
118
|
+
/* NOT FOR BROWSER ONLY END */
|
|
72
119
|
if (mt?.[1]) {
|
|
73
120
|
const [, { length }] = mt;
|
|
74
121
|
mt[0] = mt[0].slice(length);
|
|
75
122
|
mt.index += length;
|
|
76
123
|
}
|
|
77
124
|
const { 0: syntax, index: curIndex } = mt ?? { 0: '\n', index: wikitext.length }, top = stack.pop() ?? {}, { 0: open, index, parts, findEqual: topFindEqual, pos: topPos } = top, innerEqual = syntax === '=' && topFindEqual;
|
|
78
|
-
/**
|
|
79
|
-
* 填入模板内容
|
|
80
|
-
* @param text wikitext全文
|
|
81
|
-
*/
|
|
82
|
-
const push = (text) => {
|
|
83
|
-
parts[parts.length - 1].push(restore(text.slice(topPos, curIndex)));
|
|
84
|
-
};
|
|
85
125
|
if (syntax === ']]' || syntax === '}-') { // 情形1:闭合内链或转换
|
|
86
126
|
lastIndex = curIndex + 2;
|
|
87
127
|
}
|
|
@@ -92,17 +132,19 @@ const parseBraces = (wikitext, config, accum) => {
|
|
|
92
132
|
const rmt = /^(={1,6})(.+)\1((?:\s|\0\d+[cn]\x7F)*)$/u
|
|
93
133
|
.exec(wikitext.slice(index, curIndex));
|
|
94
134
|
if (rmt) {
|
|
95
|
-
wikitext = `${wikitext.slice(0, index)}\0${accum.length}h\x7F${wikitext.slice(curIndex)}`;
|
|
96
|
-
lastIndex = index + 4 + String(accum.length).length;
|
|
97
135
|
rmt[2] = restore(rmt[2]);
|
|
98
|
-
|
|
99
|
-
|
|
136
|
+
if (!rmt[2].includes('\n')) {
|
|
137
|
+
wikitext = `${wikitext.slice(0, index)}\0${accum.length}h\x7F${wikitext.slice(curIndex)}`;
|
|
138
|
+
lastIndex = index + 4 + String(accum.length).length;
|
|
139
|
+
// @ts-expect-error abstract class
|
|
140
|
+
new heading_1.HeadingToken(rmt[1].length, rmt.slice(2), config, accum);
|
|
141
|
+
}
|
|
100
142
|
}
|
|
101
143
|
}
|
|
102
144
|
}
|
|
103
145
|
else if (syntax === '|' || innerEqual) { // 情形3:模板内部,含行首单个'='
|
|
104
146
|
lastIndex = curIndex + 1;
|
|
105
|
-
push(wikitext);
|
|
147
|
+
push(wikitext, parts, topPos, curIndex);
|
|
106
148
|
if (syntax === '|') {
|
|
107
149
|
parts.push([]);
|
|
108
150
|
}
|
|
@@ -113,7 +155,7 @@ const parseBraces = (wikitext, config, accum) => {
|
|
|
113
155
|
else if (syntax.startsWith('}}')) { // 情形4:闭合模板
|
|
114
156
|
const close = syntax.slice(0, Math.min(open.length, 3)), rest = open.length - close.length, { length } = accum;
|
|
115
157
|
lastIndex = curIndex + close.length; // 这不是最终的lastIndex
|
|
116
|
-
push(wikitext);
|
|
158
|
+
push(wikitext, parts, topPos, curIndex);
|
|
117
159
|
let skip = false, ch = 't';
|
|
118
160
|
if (close.length === 3) {
|
|
119
161
|
const argParts = parts.map(part => part.join('=')), str = argParts.length > 1 && (0, string_1.removeComment)(argParts[1]).trim();
|
|
@@ -167,9 +209,9 @@ const parseBraces = (wikitext, config, accum) => {
|
|
|
167
209
|
curTop = stack[stack.length - 1];
|
|
168
210
|
}
|
|
169
211
|
}
|
|
170
|
-
regex =
|
|
212
|
+
regex = getExecRegex(source
|
|
171
213
|
+ (moreBraces ? openBraces : '')
|
|
172
|
-
+ (curTop ? `|${closes[curTop[0][0]]}${curTop.findEqual ? '|=' : ''}` : '')
|
|
214
|
+
+ (curTop ? `|${closes[curTop[0][0]]}${curTop.findEqual ? '|=' : ''}` : ''));
|
|
173
215
|
regex.lastIndex = lastIndex;
|
|
174
216
|
mt = regex.exec(wikitext);
|
|
175
217
|
}
|
|
@@ -1,27 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseCommentAndExt = void 0;
|
|
4
|
+
const common_1 = require("@bhsd/common");
|
|
4
5
|
const onlyinclude_1 = require("../src/onlyinclude");
|
|
5
6
|
const noinclude_1 = require("../src/nowiki/noinclude");
|
|
6
7
|
const include_1 = require("../src/tagPair/include");
|
|
7
8
|
const ext_1 = require("../src/tagPair/ext");
|
|
8
9
|
const comment_1 = require("../src/nowiki/comment");
|
|
9
|
-
const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', { length } = onlyincludeLeft,
|
|
10
|
-
/**
|
|
11
|
-
* 获取正则表达式
|
|
12
|
-
* @param ext 扩展标签
|
|
13
|
-
* @param includeOnly 是否嵌入
|
|
14
|
-
*/
|
|
15
|
-
const getRegex = (ext, includeOnly) => {
|
|
16
|
-
const regex = includeOnly ? regexInclude : regexNoinclude;
|
|
17
|
-
if (regex.has(ext)) {
|
|
18
|
-
return regex.get(ext);
|
|
19
|
-
}
|
|
10
|
+
const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', { length } = onlyincludeLeft, getRegex = [false, true].map(includeOnly => {
|
|
20
11
|
const noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return re;
|
|
24
|
-
};
|
|
12
|
+
return (0, common_1.getRegex)(ext => new RegExp(String.raw `<!--[\s\S]*?(?:-->|$)|<${noincludeRegex}(?:\s[^>]*)?/?>|</${noincludeRegex}\s*>|<(${ext.join('|')})(\s[^>]*?)?(?:/>|>([\s\S]*?)</(\1\s*)>)|<(${includeRegex})(\s[^>]*?)?(?:/>|>([\s\S]*?)(?:</(${includeRegex}\s*)>|$))`, 'giu'));
|
|
13
|
+
});
|
|
25
14
|
/**
|
|
26
15
|
* 更新`<onlyinclude>`和`</onlyinclude>`的位置
|
|
27
16
|
* @param wikitext
|
|
@@ -67,7 +56,7 @@ const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
|
|
|
67
56
|
return str;
|
|
68
57
|
}
|
|
69
58
|
}
|
|
70
|
-
return wikitext.replace(getRegex(config.ext
|
|
59
|
+
return wikitext.replace(getRegex[includeOnly ? 1 : 0](config.ext), (substr, name, attr, inner, closing, include, includeAttr, includeInner, includeClosing) => {
|
|
71
60
|
const l = accum.length;
|
|
72
61
|
let ch = 'n';
|
|
73
62
|
if (name) {
|
package/dist/parser/links.js
CHANGED
|
@@ -50,7 +50,7 @@ const parseLinks = (wikitext, config, accum, tidy) => {
|
|
|
50
50
|
s += `[[${x}`;
|
|
51
51
|
continue;
|
|
52
52
|
}
|
|
53
|
-
const { ns, valid, } = index_1.default.normalizeTitle(link, 0, false, config, true, true, true, true);
|
|
53
|
+
const { ns, valid, } = index_1.default.normalizeTitle(link, 0, false, config, { halfParsed: true, temporary: true, decode: true, selfLink: true });
|
|
54
54
|
if (!valid) {
|
|
55
55
|
s += `[[${x}`;
|
|
56
56
|
continue;
|
package/dist/parser/redirect.js
CHANGED
|
@@ -16,9 +16,7 @@ const parseRedirect = (text, config, accum) => {
|
|
|
16
16
|
config.regexRedirect ??= new RegExp(String.raw `^(\s*)((?:${config.redirection.join('|')})\s*(?::\s*)?)\[\[([^\n|\]]+)(\|.*?)?\]\](\s*)`, 'iu');
|
|
17
17
|
const mt = config.regexRedirect.exec(text);
|
|
18
18
|
if (mt
|
|
19
|
-
&& index_1.default
|
|
20
|
-
.normalizeTitle(mt[3], 0, false, config, true, true, true)
|
|
21
|
-
.valid) {
|
|
19
|
+
&& index_1.default.normalizeTitle(mt[3], 0, false, config, { halfParsed: true, temporary: true, decode: true }).valid) {
|
|
22
20
|
text = `\0${accum.length}o\x7F${text.slice(mt[0].length)}`;
|
|
23
21
|
// @ts-expect-error abstract class
|
|
24
22
|
new redirect_1.RedirectToken(...mt.slice(1), config, accum);
|
package/dist/src/arg.js
CHANGED
|
@@ -61,7 +61,7 @@ class ArgToken extends index_1.Token {
|
|
|
61
61
|
/** 设置name */
|
|
62
62
|
#setName() {
|
|
63
63
|
// eslint-disable-next-line no-unused-labels
|
|
64
|
-
LSP: this.setAttribute('name', this.firstChild.
|
|
64
|
+
LSP: this.setAttribute('name', this.firstChild.text().trim());
|
|
65
65
|
}
|
|
66
66
|
/** @private */
|
|
67
67
|
afterBuild() {
|
package/dist/src/attribute.js
CHANGED
|
@@ -80,7 +80,7 @@ class AttributeToken extends index_2.Token {
|
|
|
80
80
|
if (this.parentNode) {
|
|
81
81
|
this.#tag = this.parentNode.name;
|
|
82
82
|
}
|
|
83
|
-
this.setAttribute('name', this.firstChild.
|
|
83
|
+
this.setAttribute('name', this.firstChild.text().trim().toLowerCase());
|
|
84
84
|
super.afterBuild();
|
|
85
85
|
}
|
|
86
86
|
/** @private */
|
package/dist/src/converter.js
CHANGED
|
@@ -22,7 +22,7 @@ class ConverterToken extends index_1.Token {
|
|
|
22
22
|
constructor(flags, rules, config, accum = []) {
|
|
23
23
|
super(undefined, config, accum);
|
|
24
24
|
// @ts-expect-error abstract class
|
|
25
|
-
this.
|
|
25
|
+
this.insertAt(new converterFlags_1.ConverterFlagsToken(flags, config, accum));
|
|
26
26
|
const [firstRule] = rules, hasColon = firstRule.includes(':'),
|
|
27
27
|
// @ts-expect-error abstract class
|
|
28
28
|
firstRuleToken = new converterRule_1.ConverterRuleToken(firstRule, hasColon, config, accum);
|
package/dist/src/gallery.js
CHANGED
|
@@ -44,7 +44,7 @@ class GalleryToken extends index_2.Token {
|
|
|
44
44
|
* @param file 文件名
|
|
45
45
|
*/
|
|
46
46
|
#checkFile(file) {
|
|
47
|
-
return this.normalizeTitle(file, 6, true, true, true).valid;
|
|
47
|
+
return this.normalizeTitle(file, 6, { halfParsed: true, temporary: true, decode: true }).valid;
|
|
48
48
|
}
|
|
49
49
|
/** @private */
|
|
50
50
|
toString(skip) {
|
package/dist/src/heading.js
CHANGED
|
@@ -35,7 +35,7 @@ class HeadingToken extends index_2.Token {
|
|
|
35
35
|
const token = new index_2.Token(input[0], config, accum);
|
|
36
36
|
token.type = 'heading-title';
|
|
37
37
|
token.setAttribute('stage', 2);
|
|
38
|
-
const trail = new syntax_1.SyntaxToken(input[1],
|
|
38
|
+
const trail = new syntax_1.SyntaxToken(input[1], 'heading-trail', config, accum);
|
|
39
39
|
this.append(token, trail);
|
|
40
40
|
}
|
|
41
41
|
/** 标题格式的等号 */
|
|
@@ -102,7 +102,7 @@ class HeadingToken extends index_2.Token {
|
|
|
102
102
|
left: rect.left + level,
|
|
103
103
|
}, 'format-leakage', index_1.default.msg('unbalanced $1 in a section header', 'bold apostrophes')), end = start + level + innerStr.length;
|
|
104
104
|
if (rootStr.slice(e.endIndex, end).trim()) {
|
|
105
|
-
e.suggestions = [{ desc: 'close', range: [end, end], text:
|
|
105
|
+
e.suggestions = [{ desc: 'close', range: [end, end], text: `'''` }];
|
|
106
106
|
}
|
|
107
107
|
else {
|
|
108
108
|
e.fix = { desc: 'remove', range: [e.startIndex, e.endIndex], text: '' };
|
|
@@ -112,7 +112,7 @@ class HeadingToken extends index_2.Token {
|
|
|
112
112
|
if (italicQuotes.length % 2) {
|
|
113
113
|
const e = (0, lint_1.generateForChild)(italicQuotes[italicQuotes.length - 1], { start: start + level }, 'format-leakage', index_1.default.msg('unbalanced $1 in a section header', 'italic apostrophes')), end = start + level + innerStr.length;
|
|
114
114
|
e.fix = rootStr.slice(e.endIndex, end).trim()
|
|
115
|
-
? { desc: 'close', range: [end, end], text:
|
|
115
|
+
? { desc: 'close', range: [end, end], text: `''` }
|
|
116
116
|
: { desc: 'remove', range: [e.startIndex, e.endIndex], text: '' };
|
|
117
117
|
errors.push(e);
|
|
118
118
|
}
|
|
@@ -4,10 +4,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ImageParameterToken = exports.galleryParams = void 0;
|
|
7
|
+
const common_1 = require("@bhsd/common");
|
|
7
8
|
const string_1 = require("../util/string");
|
|
8
9
|
const lint_1 = require("../util/lint");
|
|
9
10
|
const index_1 = __importDefault(require("../index"));
|
|
10
11
|
const index_2 = require("./index");
|
|
12
|
+
const getUrlLikeRegex = (0, common_1.getRegex)(protocol => new RegExp(String.raw `^(?:${protocol}|//|\0\d+m\x7F)`, 'iu'));
|
|
13
|
+
const getUrlRegex = (0, common_1.getRegex)(protocol => new RegExp(String.raw `^(?:(?:${protocol}|//)${string_1.extUrlCharFirst}|\0\d+m\x7F)${string_1.extUrlChar}$`, 'iu'));
|
|
14
|
+
const getSyntaxRegex = (0, common_1.getRegex)(syntax => new RegExp(String.raw `^(\s*(?!\s))${syntax.replace('$1', '(.*)')}${syntax.endsWith('$1') ? '(?=$|\n)' : ''}(\s*)$`, 'u'));
|
|
11
15
|
exports.galleryParams = new Set(['alt', 'link', 'lang', 'page', 'caption']);
|
|
12
16
|
function validate(key, val, config, halfParsed, ext) {
|
|
13
17
|
val = val.trim();
|
|
@@ -19,15 +23,13 @@ function validate(key, val, config, halfParsed, ext) {
|
|
|
19
23
|
if (!value) {
|
|
20
24
|
return val;
|
|
21
25
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (re1.test(value)) {
|
|
25
|
-
return re2.test(value) && val;
|
|
26
|
+
else if (getUrlLikeRegex(config.protocol).test(value)) {
|
|
27
|
+
return getUrlRegex(config.protocol).test(value) && val;
|
|
26
28
|
}
|
|
27
29
|
else if (value.startsWith('[[') && value.endsWith(']]')) {
|
|
28
30
|
value = value.slice(2, -2);
|
|
29
31
|
}
|
|
30
|
-
const title = index_1.default.normalizeTitle(value, 0, false, config,
|
|
32
|
+
const title = index_1.default.normalizeTitle(value, 0, false, config, { halfParsed, decode: true, selfLink: true });
|
|
31
33
|
return title.valid && title;
|
|
32
34
|
}
|
|
33
35
|
case 'lang':
|
|
@@ -59,10 +61,8 @@ class ImageParameterToken extends index_2.Token {
|
|
|
59
61
|
/** @param str 图片参数 */
|
|
60
62
|
constructor(str, extension, config, accum) {
|
|
61
63
|
let mt;
|
|
62
|
-
const regexes = Object.entries(config.img)
|
|
63
|
-
|
|
64
|
-
return [syntax, param, re];
|
|
65
|
-
}), param = regexes.find(([, key, regex]) => {
|
|
64
|
+
const regexes = Object.entries(config.img)
|
|
65
|
+
.map(([syntax, param]) => [syntax, param, getSyntaxRegex(syntax)]), param = regexes.find(([, key, regex]) => {
|
|
66
66
|
mt = regex.exec(str);
|
|
67
67
|
return mt
|
|
68
68
|
&& (mt.length !== 4
|
package/dist/src/imagemap.js
CHANGED
|
@@ -38,7 +38,7 @@ class ImagemapToken extends index_2.Token {
|
|
|
38
38
|
//
|
|
39
39
|
}
|
|
40
40
|
else if (first) {
|
|
41
|
-
const pipe = line.indexOf('|'), file = pipe === -1 ? line : line.slice(0, pipe), { valid, ns, } = this.normalizeTitle(file, 0, true, true);
|
|
41
|
+
const pipe = line.indexOf('|'), file = pipe === -1 ? line : line.slice(0, pipe), { valid, ns, } = this.normalizeTitle(file, 0, { halfParsed: true, temporary: true });
|
|
42
42
|
if (valid
|
|
43
43
|
&& ns === 6) {
|
|
44
44
|
// @ts-expect-error abstract class
|
|
@@ -59,7 +59,8 @@ class ImagemapToken extends index_2.Token {
|
|
|
59
59
|
const i = line.indexOf('['), substr = line.slice(i), mtIn = /^\[\[([^|]+)(?:\|([^\]]+))?\]\][\w\s]*$/u
|
|
60
60
|
.exec(substr);
|
|
61
61
|
if (mtIn) {
|
|
62
|
-
if (this.normalizeTitle(mtIn[1], 0, true, true,
|
|
62
|
+
if (this.normalizeTitle(mtIn[1], 0, { halfParsed: true, temporary: true, selfLink: true })
|
|
63
|
+
.valid) {
|
|
63
64
|
// @ts-expect-error abstract class
|
|
64
65
|
super.insertAt(new imagemapLink_1.ImagemapLinkToken(line.slice(0, i), mtIn.slice(1), substr.slice(substr.indexOf(']]') + 2), config, accum));
|
|
65
66
|
continue;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import Parser from '../index';
|
|
|
2
2
|
import { AstElement } from '../lib/element';
|
|
3
3
|
import { AstText } from '../lib/text';
|
|
4
4
|
import type { LintError, TokenTypes } from '../base';
|
|
5
|
-
import type { Title } from '../lib/title';
|
|
5
|
+
import type { Title, TitleOptions } from '../lib/title';
|
|
6
6
|
import type { AstNodes } from '../internal';
|
|
7
7
|
/**
|
|
8
8
|
* base class for all tokens
|
package/dist/src/index.js
CHANGED
|
@@ -49,6 +49,7 @@ exports.Token = void 0;
|
|
|
49
49
|
const string_1 = require("../util/string");
|
|
50
50
|
const constants_1 = require("../util/constants");
|
|
51
51
|
const lint_1 = require("../util/lint");
|
|
52
|
+
const debug_1 = require("../util/debug");
|
|
52
53
|
const index_1 = __importDefault(require("../index"));
|
|
53
54
|
const element_1 = require("../lib/element");
|
|
54
55
|
const text_1 = require("../lib/text");
|
|
@@ -168,7 +169,7 @@ class Token extends element_1.AstElement {
|
|
|
168
169
|
this.#stage = constants_1.MAX_STAGE;
|
|
169
170
|
const { length, firstChild } = this, str = firstChild?.toString();
|
|
170
171
|
if (length === 1 && firstChild.type === 'text' && str.includes('\0')) {
|
|
171
|
-
|
|
172
|
+
(0, debug_1.setChildNodes)(this, 0, 1, this.buildFromStr(str));
|
|
172
173
|
this.normalize();
|
|
173
174
|
if (this.type === 'root') {
|
|
174
175
|
for (const token of this.#accum) {
|
|
@@ -317,6 +318,8 @@ class Token extends element_1.AstElement {
|
|
|
317
318
|
return this.#accum;
|
|
318
319
|
case 'built':
|
|
319
320
|
return this.#built;
|
|
321
|
+
case 'stage':
|
|
322
|
+
return this.#stage;
|
|
320
323
|
default:
|
|
321
324
|
return super.getAttribute(key);
|
|
322
325
|
}
|
|
@@ -344,9 +347,8 @@ class Token extends element_1.AstElement {
|
|
|
344
347
|
return token;
|
|
345
348
|
}
|
|
346
349
|
/** @private */
|
|
347
|
-
normalizeTitle(title, defaultNs = 0,
|
|
348
|
-
return index_1.default
|
|
349
|
-
.normalizeTitle(title, defaultNs, this.#include, this.#config, temporary, halfParsed, decode, selfLink);
|
|
350
|
+
normalizeTitle(title, defaultNs = 0, opt) {
|
|
351
|
+
return index_1.default.normalizeTitle(title, defaultNs, this.#include, this.#config, opt);
|
|
350
352
|
}
|
|
351
353
|
/** @private */
|
|
352
354
|
lint(start = this.getAbsoluteIndex(), re) {
|
|
@@ -423,7 +425,7 @@ class Token extends element_1.AstElement {
|
|
|
423
425
|
});
|
|
424
426
|
/* NOT FOR BROWSER ONLY */
|
|
425
427
|
}
|
|
426
|
-
else if ((0, lsp_1.isAttr)(this, true)) {
|
|
428
|
+
else if (index_1.default.lintCSS && (0, lsp_1.isAttr)(this, true)) {
|
|
427
429
|
const root = this.getRootNode(), textDoc = new document_1.EmbeddedCSSDocument(root, this);
|
|
428
430
|
errors.push(...document_1.cssLSP.doValidation(textDoc, textDoc.styleSheet)
|
|
429
431
|
.filter(({ code }) => code !== 'css-ruleorselectorexpected')
|
package/dist/src/link/base.js
CHANGED
|
@@ -132,7 +132,7 @@ class LinkBaseToken extends index_2.Token {
|
|
|
132
132
|
}
|
|
133
133
|
/** @private */
|
|
134
134
|
getTitle(temporary, halfParsed) {
|
|
135
|
-
return this.normalizeTitle(this.firstChild.
|
|
135
|
+
return this.normalizeTitle(this.firstChild.text(), 0, { halfParsed, temporary, decode: true, selfLink: true });
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
exports.LinkBaseToken = LinkBaseToken;
|
package/dist/src/link/file.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export declare abstract class FileToken extends LinkBaseToken {
|
|
|
13
13
|
readonly childNodes: readonly [AtomToken, ...ImageParameterToken[]];
|
|
14
14
|
abstract get lastChild(): AtomToken | ImageParameterToken;
|
|
15
15
|
get type(): 'file' | 'gallery-image' | 'imagemap-image';
|
|
16
|
+
/** file extension / 扩展名 */
|
|
17
|
+
get extension(): string | undefined;
|
|
16
18
|
/**
|
|
17
19
|
* @param link 文件名
|
|
18
20
|
* @param text 图片参数
|
package/dist/src/link/file.js
CHANGED
|
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.FileToken = void 0;
|
|
7
|
-
const string_1 = require("../../util/string");
|
|
8
7
|
const lint_1 = require("../../util/lint");
|
|
9
8
|
const rect_1 = require("../../lib/rect");
|
|
10
9
|
const index_1 = __importDefault(require("../../index"));
|
|
@@ -15,24 +14,21 @@ const frame = new Map([
|
|
|
15
14
|
['frameless', 'Frameless'],
|
|
16
15
|
['framed', 'Frame'],
|
|
17
16
|
['thumbnail', 'Thumb'],
|
|
18
|
-
]), horizAlign = new Set(['left', 'right', 'center', 'none']), vertAlign = new Set(['baseline', 'sub', 'super', 'top', 'text-top', 'middle', 'bottom', 'text-bottom']);
|
|
17
|
+
]), horizAlign = new Set(['left', 'right', 'center', 'none']), vertAlign = new Set(['baseline', 'sub', 'super', 'top', 'text-top', 'middle', 'bottom', 'text-bottom']), extensions = new Set(['tiff', 'tif', 'png', 'gif', 'jpg', 'jpeg', 'webp', 'xcf', 'pdf', 'svg', 'djvu']);
|
|
19
18
|
/**
|
|
20
19
|
* a more sophisticated string-explode function
|
|
21
|
-
* @param start start syntax of a nested AST node
|
|
22
|
-
* @param end end syntax of a nested AST node
|
|
23
|
-
* @param separator syntax for explosion
|
|
24
20
|
* @param str string to be exploded
|
|
25
21
|
*/
|
|
26
|
-
const explode = (
|
|
22
|
+
const explode = (str) => {
|
|
27
23
|
if (str === undefined) {
|
|
28
24
|
return [];
|
|
29
25
|
}
|
|
30
|
-
const regex =
|
|
26
|
+
const regex = /-\{|\}-|\|/gu, exploded = [];
|
|
31
27
|
let mt = regex.exec(str), depth = 0, lastIndex = 0;
|
|
32
28
|
while (mt) {
|
|
33
29
|
const { 0: match, index } = mt;
|
|
34
|
-
if (match !==
|
|
35
|
-
depth += match ===
|
|
30
|
+
if (match !== '|') {
|
|
31
|
+
depth += match === '-{' ? 1 : -1;
|
|
36
32
|
}
|
|
37
33
|
else if (depth === 0) {
|
|
38
34
|
exploded.push(str.slice(lastIndex, index));
|
|
@@ -53,6 +49,10 @@ class FileToken extends base_1.LinkBaseToken {
|
|
|
53
49
|
get type() {
|
|
54
50
|
return 'file';
|
|
55
51
|
}
|
|
52
|
+
/** file extension / 扩展名 */
|
|
53
|
+
get extension() {
|
|
54
|
+
return this.getAttribute('title').extension;
|
|
55
|
+
}
|
|
56
56
|
/**
|
|
57
57
|
* @param link 文件名
|
|
58
58
|
* @param text 图片参数
|
|
@@ -61,7 +61,7 @@ class FileToken extends base_1.LinkBaseToken {
|
|
|
61
61
|
constructor(link, text, config = index_1.default.getConfig(), accum = [], delimiter = '|') {
|
|
62
62
|
super(link, undefined, config, accum, delimiter);
|
|
63
63
|
const { extension } = this.getTitle(true, true);
|
|
64
|
-
this.append(...explode(
|
|
64
|
+
this.append(...explode(text).map(
|
|
65
65
|
// @ts-expect-error abstract class
|
|
66
66
|
(part) => new imageParameter_1.ImageParameterToken(part, extension, config, accum)));
|
|
67
67
|
}
|
|
@@ -92,12 +92,14 @@ class FileToken extends base_1.LinkBaseToken {
|
|
|
92
92
|
* 图片参数到语法错误的映射
|
|
93
93
|
* @param msg 消息键
|
|
94
94
|
* @param p1 替换$1
|
|
95
|
+
* @param severity 错误等级
|
|
95
96
|
*/
|
|
96
|
-
const generate = (msg, p1) => (arg) => {
|
|
97
|
-
const e = (0, lint_1.generateForChild)(arg, rect, 'no-duplicate', index_1.default.msg(`${msg} image $1 parameter`, p1));
|
|
97
|
+
const generate = (msg, p1, severity) => (arg) => {
|
|
98
|
+
const e = (0, lint_1.generateForChild)(arg, rect, 'no-duplicate', index_1.default.msg(`${msg} image $1 parameter`, p1), severity);
|
|
98
99
|
e.suggestions = [{ desc: 'remove', range: [e.startIndex - 1, e.endIndex], text: '' }];
|
|
99
100
|
return e;
|
|
100
101
|
};
|
|
102
|
+
const { extension } = this;
|
|
101
103
|
for (const key of keys) {
|
|
102
104
|
if (key === 'invalid' || key === 'width' && unscaled) {
|
|
103
105
|
continue;
|
|
@@ -110,7 +112,7 @@ class FileToken extends base_1.LinkBaseToken {
|
|
|
110
112
|
];
|
|
111
113
|
}
|
|
112
114
|
if (relevantArgs.length > 1) {
|
|
113
|
-
errors.push(...relevantArgs.map(generate('duplicated', key)));
|
|
115
|
+
errors.push(...relevantArgs.map(generate('duplicated', key, key === 'caption' && extension && !extensions.has(extension) ? 'warning' : 'error')));
|
|
114
116
|
}
|
|
115
117
|
}
|
|
116
118
|
if (frameKeys.length > 1) {
|
|
@@ -43,7 +43,7 @@ class GalleryImageToken extends file_1.FileToken {
|
|
|
43
43
|
/** @private */
|
|
44
44
|
getTitle(temporary) {
|
|
45
45
|
const imagemap = this.type === 'imagemap-image';
|
|
46
|
-
return this.normalizeTitle(this.firstChild.toString(), imagemap ? 0 : 6,
|
|
46
|
+
return this.normalizeTitle(this.firstChild.toString(), imagemap ? 0 : 6, { halfParsed: true, temporary, decode: !imagemap });
|
|
47
47
|
}
|
|
48
48
|
/** @private */
|
|
49
49
|
getAttribute(key) {
|
|
@@ -27,8 +27,7 @@ class RedirectTargetToken extends base_1.LinkBaseToken {
|
|
|
27
27
|
}
|
|
28
28
|
/** @private */
|
|
29
29
|
getTitle() {
|
|
30
|
-
return this
|
|
31
|
-
.normalizeTitle(this.firstChild.toString(), 0, false, true, true);
|
|
30
|
+
return this.normalizeTitle(this.firstChild.toString(), 0, { halfParsed: true, decode: true });
|
|
32
31
|
}
|
|
33
32
|
/** @private */
|
|
34
33
|
lint(start = this.getAbsoluteIndex()) {
|
package/dist/src/magicLink.js
CHANGED
|
@@ -95,7 +95,7 @@ class MagicLinkToken extends index_2.Token {
|
|
|
95
95
|
if (type === 'magic-link') {
|
|
96
96
|
if (link.startsWith('ISBN')) {
|
|
97
97
|
return this
|
|
98
|
-
.normalizeTitle(`BookSources/${link.slice(5)}`, -1, true)
|
|
98
|
+
.normalizeTitle(`BookSources/${link.slice(5)}`, -1, { temporary: true })
|
|
99
99
|
.getUrl(articlePath);
|
|
100
100
|
}
|
|
101
101
|
link = link.startsWith('RFC')
|
package/dist/src/nested.js
CHANGED
|
@@ -12,7 +12,10 @@ const index_1 = __importDefault(require("../index"));
|
|
|
12
12
|
const index_2 = require("./index");
|
|
13
13
|
const ext_1 = require("./tagPair/ext");
|
|
14
14
|
const noinclude_1 = require("./nowiki/noinclude");
|
|
15
|
-
const childTypes = new Set(['comment', 'include', 'arg', 'template', 'magic-word'])
|
|
15
|
+
const childTypes = new Set(['comment', 'include', 'arg', 'template', 'magic-word']), lintRegex = [false, true].map(article => {
|
|
16
|
+
const noinclude = article ? 'includeonly' : 'noinclude';
|
|
17
|
+
return new RegExp(String.raw `^(?:<${noinclude}(?:\s[^>]*)?/?>|</${noinclude}\s*>)$`, 'iu');
|
|
18
|
+
});
|
|
16
19
|
/**
|
|
17
20
|
* extension tag that has a nested structure
|
|
18
21
|
*
|
|
@@ -56,10 +59,7 @@ class NestedToken extends index_2.Token {
|
|
|
56
59
|
}
|
|
57
60
|
/** @private */
|
|
58
61
|
lint(start = this.getAbsoluteIndex(), re) {
|
|
59
|
-
const rect = new rect_1.BoundingRect(this, start),
|
|
60
|
-
const regex = typeof this.#regex === 'boolean'
|
|
61
|
-
? new RegExp(String.raw `^(?:<${noinclude}(?:\s[^>]*)?/?>|</${noinclude}\s*>)$`, 'iu')
|
|
62
|
-
: /^<!--[\s\S]*-->$/u;
|
|
62
|
+
const rect = new rect_1.BoundingRect(this, start), regex = typeof this.#regex === 'boolean' ? lintRegex[this.#regex ? 1 : 0] : /^<!--[\s\S]*-->$/u;
|
|
63
63
|
return [
|
|
64
64
|
...super.lint(start, re),
|
|
65
65
|
...this.childNodes.filter(child => {
|
package/dist/src/nowiki/index.js
CHANGED
|
@@ -4,9 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.NowikiToken = void 0;
|
|
7
|
+
const common_1 = require("@bhsd/common");
|
|
7
8
|
const lint_1 = require("../../util/lint");
|
|
8
9
|
const index_1 = __importDefault(require("../../index"));
|
|
9
10
|
const base_1 = require("./base");
|
|
11
|
+
const getLintRegex = (0, common_1.getRegex)(name => new RegExp(String.raw `<\s*(?:/\s*)${name === 'nowiki' ? '' : '?'}(${name})\b`, 'giu'));
|
|
10
12
|
/**
|
|
11
13
|
* text-only token inside an extension tag
|
|
12
14
|
*
|
|
@@ -24,8 +26,7 @@ class NowikiToken extends base_1.NowikiBaseToken {
|
|
|
24
26
|
e.fix = { range: [start, e.endIndex], text: '', desc: 'empty' };
|
|
25
27
|
return [e];
|
|
26
28
|
}
|
|
27
|
-
|
|
28
|
-
return super.lint(start, re);
|
|
29
|
+
return super.lint(start, getLintRegex(name));
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
exports.NowikiToken = NowikiToken;
|
package/dist/src/redirect.js
CHANGED
|
@@ -76,8 +76,7 @@ let RedirectToken = (() => {
|
|
|
76
76
|
super(undefined, config, accum);
|
|
77
77
|
this.#pre = pre;
|
|
78
78
|
this.#post = post;
|
|
79
|
-
|
|
80
|
-
this.append(new syntax_1.SyntaxToken(syntax, pattern, 'redirect-syntax', config, accum, {}),
|
|
79
|
+
this.append(new syntax_1.SyntaxToken(syntax, 'redirect-syntax', config, accum),
|
|
81
80
|
// @ts-expect-error abstract class
|
|
82
81
|
new redirectTarget_1.RedirectTargetToken(link, text?.slice(1), config, accum));
|
|
83
82
|
}
|