wikilint 2.3.7 → 2.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base.d.ts +5 -1
- package/dist/lib/title.js +2 -2
- package/dist/mixin/hidden.js +0 -1
- package/dist/parser/braces.js +1 -1
- package/dist/parser/commentAndExt.js +6 -1
- package/dist/parser/converter.js +4 -4
- package/dist/parser/externalLinks.js +4 -1
- package/dist/parser/magicLinks.js +2 -0
- package/dist/src/attribute.js +29 -5
- package/dist/src/attributes.js +3 -3
- package/dist/src/heading.js +16 -7
- package/dist/src/html.d.ts +1 -1
- package/dist/src/html.js +37 -3
- package/dist/src/imageParameter.js +2 -0
- package/dist/src/imagemap.js +1 -1
- package/dist/src/link/base.js +4 -0
- package/dist/src/nowiki/quote.d.ts +4 -0
- package/dist/src/nowiki/quote.js +8 -0
- package/dist/src/parameter.js +2 -0
- package/dist/src/tagPair/ext.d.ts +1 -0
- package/dist/src/tagPair/index.d.ts +2 -2
- package/dist/src/tagPair/index.js +7 -11
- package/dist/src/transclude.d.ts +1 -1
- package/dist/src/transclude.js +4 -9
- package/i18n/zh-hans.json +6 -1
- package/i18n/zh-hant.json +6 -1
- package/package.json +1 -1
package/dist/base.d.ts
CHANGED
|
@@ -28,6 +28,9 @@ export interface AstNode {
|
|
|
28
28
|
/** Linter */
|
|
29
29
|
lint(): LintError[];
|
|
30
30
|
}
|
|
31
|
+
/** 类似HTMLElement */
|
|
32
|
+
interface AstElement extends AstNode {
|
|
33
|
+
}
|
|
31
34
|
export interface Parser {
|
|
32
35
|
config: string | Config;
|
|
33
36
|
i18n: string | Record<string, string> | undefined;
|
|
@@ -36,5 +39,6 @@ export interface Parser {
|
|
|
36
39
|
* @param include 是否嵌入
|
|
37
40
|
* @param maxStage 最大解析层级
|
|
38
41
|
*/
|
|
39
|
-
parse(wikitext: string, include?: boolean, maxStage?: number, config?: Config):
|
|
42
|
+
parse(wikitext: string, include?: boolean, maxStage?: number, config?: Config): AstElement;
|
|
40
43
|
}
|
|
44
|
+
export {};
|
package/dist/lib/title.js
CHANGED
|
@@ -36,7 +36,7 @@ class Title {
|
|
|
36
36
|
const m = title.split(':');
|
|
37
37
|
if (m.length > 1) {
|
|
38
38
|
const id = nsid[m[0].trim().toLowerCase()];
|
|
39
|
-
if (id
|
|
39
|
+
if (id) {
|
|
40
40
|
ns = id;
|
|
41
41
|
title = m.slice(1).join(':').trim();
|
|
42
42
|
}
|
|
@@ -50,7 +50,7 @@ class Title {
|
|
|
50
50
|
title = title.slice(0, i).trim();
|
|
51
51
|
}
|
|
52
52
|
this.valid = Boolean(title
|
|
53
|
-
|| selfLink && this.fragment !== undefined) &&
|
|
53
|
+
|| selfLink && this.fragment !== undefined) && !/^:|\0\d+[eh!+-]\x7F|[<>[\]{}|]|%[\da-f]{2}/iu.test(title);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
exports.Title = Title;
|
package/dist/mixin/hidden.js
CHANGED
package/dist/parser/braces.js
CHANGED
|
@@ -84,7 +84,7 @@ const parseBraces = (wikitext, config = Parser.getConfig(), accum = []) => {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
catch (e) {
|
|
87
|
-
if (e instanceof SyntaxError && e.message
|
|
87
|
+
if (e instanceof SyntaxError && e.message === '非法的模板名称') {
|
|
88
88
|
skip = true;
|
|
89
89
|
}
|
|
90
90
|
else {
|
|
@@ -49,9 +49,14 @@ const parseCommentAndExt = (wikitext, config = Parser.getConfig(), accum = [], i
|
|
|
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 */
|
|
52
57
|
const ext = config.ext.join('|'), noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly', regex = new RegExp('<!--.*?(?:-->|$)' // comment
|
|
53
58
|
+ '|'
|
|
54
|
-
+ `<${noincludeRegex}(?:\\s[^>]
|
|
59
|
+
+ `<${noincludeRegex}(?:\\s[^>]*)?>|</${noincludeRegex}\\s*>` // <noinclude>
|
|
55
60
|
+ '|'
|
|
56
61
|
+ `<(${ext})(\\s[^>]*?)?(?:/>|>(.*?)</(\\1\\s*)>)` // 扩展标签
|
|
57
62
|
+ '|'
|
package/dist/parser/converter.js
CHANGED
|
@@ -15,10 +15,10 @@ const parseConverter = (text, config = Parser.getConfig(), accum = []) => {
|
|
|
15
15
|
while (mt) {
|
|
16
16
|
const { 0: syntax, index } = mt;
|
|
17
17
|
if (syntax === '}-') {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
19
|
+
/;(?=(?:[^;]*?=>)?\s*foo\s*:|(?:\s|\0\d+c\x7F)*$)/u;
|
|
20
|
+
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'))
|
|
21
|
+
.map(rule => rule.replace(/\x01/gu, ';'));
|
|
22
22
|
new converter_1.ConverterToken(flags, rules, config, accum);
|
|
23
23
|
text = `${text.slice(0, top.index)}\0${length}v\x7F${text.slice(index + 2)}`;
|
|
24
24
|
if (stack.length === 0) {
|
|
@@ -11,7 +11,10 @@ const extLink_1 = require("../src/extLink");
|
|
|
11
11
|
* @param accum
|
|
12
12
|
*/
|
|
13
13
|
const parseExternalLinks = (wikitext, config = Parser.getConfig(), accum = []) => {
|
|
14
|
-
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
15
|
+
/\[((?:\[[\da-f:.]+\]|[^[\]\t\n\p{Zs}])[^[\]\t\n\p{Zs}]*(?=[[\]\t\p{Zs}]|\0\d))(\p{Zs}*(?=\P{Zs}))([^\]\n]*)\]/giu;
|
|
16
|
+
const regex = new RegExp(`\\[((?:(?:${config.protocol}|//)${string_1.extUrlCharFirst}|\0\\d+m\x7F)${string_1.extUrlChar}(?=[[\\]<>"\\t\\p{Zs}]|\0\\d))`
|
|
17
|
+
+ '(\\p{Zs}*(?=\\P{Zs}))([^\\]\x01-\x08\x0A-\x1F\uFFFD]*)\\]', 'giu');
|
|
15
18
|
return wikitext.replace(regex, (_, url, space, text) => {
|
|
16
19
|
const { length } = accum, mt = /&[lg]t;/u.exec(url);
|
|
17
20
|
if (mt) {
|
|
@@ -11,6 +11,8 @@ const magicLink_1 = require("../src/magicLink");
|
|
|
11
11
|
* @param accum
|
|
12
12
|
*/
|
|
13
13
|
const parseMagicLinks = (wikitext, config = Parser.getConfig(), accum = []) => {
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
15
|
+
/(^|[^\p{L}\d_])((?:\[[\da-f:.]+\]|[^[\]<>"\t\n\p{Zs}])(?:[^[\]<>"\0\t\n\p{Zs}]|\0\d+c\x7F)*)/giu;
|
|
14
16
|
const regex = new RegExp(`(^|[^\\p{L}\\d_])(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})`, 'giu');
|
|
15
17
|
return wikitext.replace(regex, (m, lead, p1) => {
|
|
16
18
|
let trail = '', url = lead ? m.slice(1) : m;
|
package/dist/src/attribute.js
CHANGED
|
@@ -32,7 +32,7 @@ const commonHtmlAttrs = new Set([
|
|
|
32
32
|
'itemref',
|
|
33
33
|
'itemscope',
|
|
34
34
|
'itemtype',
|
|
35
|
-
]), blockAttrs = new Set(['align']), citeAttrs = new Set(['cite']), citeAndAttrs = new Set(['cite', 'datetime']), widthAttrs = new Set(['width']),
|
|
35
|
+
]), blockAttrs = new Set(['align']), citeAttrs = new Set(['cite']), citeAndAttrs = new Set(['cite', 'datetime']), widthAttrs = new Set(['width']), obsoleteTdAttrs = new Set(['axis', 'align', 'bgcolor', 'height', 'width', 'valign']), tdAttrs = new Set([...obsoleteTdAttrs, 'abbr', 'headers', 'scope', 'rowspan', 'colspan']), typeAttrs = new Set(['type']), obsoleteTableAttrs = new Set(['summary', 'align', 'bgcolor', 'cellpadding', 'cellspacing', 'frame', 'rules', 'width']), brAttrs = new Set(['clear']), trAttrs = new Set(['bgcolor', 'align', 'valign']), htmlAttrs = {
|
|
36
36
|
div: blockAttrs,
|
|
37
37
|
h1: blockAttrs,
|
|
38
38
|
h2: blockAttrs,
|
|
@@ -43,16 +43,16 @@ const commonHtmlAttrs = new Set([
|
|
|
43
43
|
blockquote: citeAttrs,
|
|
44
44
|
q: citeAttrs,
|
|
45
45
|
p: blockAttrs,
|
|
46
|
-
br:
|
|
46
|
+
br: brAttrs,
|
|
47
47
|
pre: widthAttrs,
|
|
48
48
|
ins: citeAndAttrs,
|
|
49
49
|
del: citeAndAttrs,
|
|
50
50
|
ul: typeAttrs,
|
|
51
51
|
ol: new Set(['type', 'start', 'reversed']),
|
|
52
52
|
li: new Set(['type', 'value']),
|
|
53
|
-
table: new Set([
|
|
53
|
+
table: new Set([...obsoleteTableAttrs, 'border']),
|
|
54
54
|
caption: blockAttrs,
|
|
55
|
-
tr:
|
|
55
|
+
tr: trAttrs,
|
|
56
56
|
td: tdAttrs,
|
|
57
57
|
th: tdAttrs,
|
|
58
58
|
img: new Set(['alt', 'src', 'width', 'height', 'srcset']),
|
|
@@ -118,12 +118,32 @@ const commonHtmlAttrs = new Set([
|
|
|
118
118
|
+ '|'
|
|
119
119
|
+ '(?:url|image(?:-set)?)\\s*\\('
|
|
120
120
|
+ '|'
|
|
121
|
-
+ 'attr\\s*\\([^)]+[\\s,]url', 'u')
|
|
121
|
+
+ 'attr\\s*\\([^)]+[\\s,]url', 'u'), obsoleteAttrs = {
|
|
122
|
+
table: obsoleteTableAttrs,
|
|
123
|
+
td: new Set([...obsoleteTdAttrs, 'scope']),
|
|
124
|
+
th: obsoleteTdAttrs,
|
|
125
|
+
br: brAttrs,
|
|
126
|
+
caption: blockAttrs,
|
|
127
|
+
div: blockAttrs,
|
|
128
|
+
hr: widthAttrs,
|
|
129
|
+
h1: blockAttrs,
|
|
130
|
+
h2: blockAttrs,
|
|
131
|
+
h3: blockAttrs,
|
|
132
|
+
h4: blockAttrs,
|
|
133
|
+
h5: blockAttrs,
|
|
134
|
+
h6: blockAttrs,
|
|
135
|
+
li: typeAttrs,
|
|
136
|
+
p: blockAttrs,
|
|
137
|
+
pre: widthAttrs,
|
|
138
|
+
tr: trAttrs,
|
|
139
|
+
ul: typeAttrs,
|
|
140
|
+
};
|
|
122
141
|
/**
|
|
123
142
|
* 扩展和HTML标签属性
|
|
124
143
|
* @classdesc `{childNodes: [AtomToken, Token|AtomToken]}`
|
|
125
144
|
*/
|
|
126
145
|
class AttributeToken extends index_1.Token {
|
|
146
|
+
tag;
|
|
127
147
|
#equal;
|
|
128
148
|
#quotes;
|
|
129
149
|
/** 引号是否匹配 */
|
|
@@ -222,6 +242,10 @@ class AttributeToken extends index_1.Token {
|
|
|
222
242
|
rect ??= { start, ...this.getRootNode().posFromIndex(start) };
|
|
223
243
|
errors.push((0, lint_1.generateForChild)(firstChild, rect, 'illegal attribute name'));
|
|
224
244
|
}
|
|
245
|
+
else if (obsoleteAttrs[tag]?.has(name)) {
|
|
246
|
+
rect ??= { start, ...this.getRootNode().posFromIndex(start) };
|
|
247
|
+
errors.push((0, lint_1.generateForChild)(firstChild, rect, 'obsolete attribute', 'warning'));
|
|
248
|
+
}
|
|
225
249
|
else if (name === 'style' && typeof value === 'string' && insecureStyle.test(value)) {
|
|
226
250
|
rect ??= { start, ...this.getRootNode().posFromIndex(start) };
|
|
227
251
|
errors.push((0, lint_1.generateForChild)(lastChild, rect, 'insecure style'));
|
package/dist/src/attributes.js
CHANGED
|
@@ -32,10 +32,10 @@ class AttributesToken extends index_1.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;
|
|
35
37
|
const regex = new RegExp('([^\\s/](?:(?!\0\\d+~\x7F)[^\\s/=])*)' // 属性名
|
|
36
|
-
+ `(?:${'((?:\\s|\0\\d+c\x7F)*' //
|
|
37
|
-
+ '(?:=|\0\\d+~\x7F)' // `=`
|
|
38
|
-
+ '(?:\\s|\0\\d+c\x7F)*)' // `=`后的空白字符
|
|
38
|
+
+ `(?:${'((?:\\s|\0\\d+c\x7F)*(?:=|\0\\d+~\x7F)(?:\\s|\0\\d+c\x7F)*)' // `=`和前后的空白字符
|
|
39
39
|
+ `(?:(["'])(.*?)(\\3|$)|(\\S*))` // 属性值
|
|
40
40
|
})?`, 'gsu');
|
|
41
41
|
let out = '', mt = regex.exec(attr), lastIndex = 0;
|
package/dist/src/heading.js
CHANGED
|
@@ -55,18 +55,27 @@ class HeadingToken extends index_1.Token {
|
|
|
55
55
|
}
|
|
56
56
|
/** @override */
|
|
57
57
|
lint(start = this.getAbsoluteIndex()) {
|
|
58
|
-
const errors = super.lint(start), { firstChild } = this, innerStr = String(firstChild);
|
|
59
|
-
let
|
|
58
|
+
const errors = super.lint(start), { firstChild, level } = this, innerStr = String(firstChild), quotes = firstChild.childNodes.filter((node) => node.type === 'quote'), boldQuotes = quotes.filter(({ bold }) => bold), italicQuotes = quotes.filter(({ italic }) => italic);
|
|
59
|
+
let rect;
|
|
60
60
|
if (this.level === 1) {
|
|
61
|
-
|
|
62
|
-
errors.push(
|
|
61
|
+
rect = { start, ...this.getRootNode().posFromIndex(start) };
|
|
62
|
+
errors.push((0, lint_1.generateForChild)(firstChild, rect, '<h1>'));
|
|
63
63
|
}
|
|
64
64
|
if (innerStr.startsWith('=') || innerStr.endsWith('=')) {
|
|
65
|
-
|
|
66
|
-
errors.push(
|
|
65
|
+
rect ??= { start, ...this.getRootNode().posFromIndex(start) };
|
|
66
|
+
errors.push((0, lint_1.generateForChild)(firstChild, rect, Parser.msg('unbalanced $1 in a section header', '"="')));
|
|
67
67
|
}
|
|
68
68
|
if (this.closest('html-attrs, table-attrs')) {
|
|
69
|
-
|
|
69
|
+
rect ??= { start, ...this.getRootNode().posFromIndex(start) };
|
|
70
|
+
errors.push((0, lint_1.generateForSelf)(this, rect, 'section header in a HTML tag'));
|
|
71
|
+
}
|
|
72
|
+
if (boldQuotes.length % 2) {
|
|
73
|
+
rect ??= { start, ...this.getRootNode().posFromIndex(start) };
|
|
74
|
+
errors.push((0, lint_1.generateForChild)(boldQuotes[boldQuotes.length - 1], { ...rect, start: start + level, left: rect.left + level }, Parser.msg('unbalanced $1 in a section header', 'bold apostrophes')));
|
|
75
|
+
}
|
|
76
|
+
if (italicQuotes.length % 2) {
|
|
77
|
+
rect ??= { start, ...this.getRootNode().posFromIndex(start) };
|
|
78
|
+
errors.push((0, lint_1.generateForChild)(italicQuotes[italicQuotes.length - 1], { start: start + level }, Parser.msg('unbalanced $1 in a section header', 'italic apostrophes')));
|
|
70
79
|
}
|
|
71
80
|
return errors;
|
|
72
81
|
}
|
package/dist/src/html.d.ts
CHANGED
package/dist/src/html.js
CHANGED
|
@@ -5,7 +5,33 @@ const lint_1 = require("../util/lint");
|
|
|
5
5
|
const string_1 = require("../util/string");
|
|
6
6
|
const Parser = require("../index");
|
|
7
7
|
const index_1 = require("./index");
|
|
8
|
-
const magicWords = new Set(['if', 'ifeq', 'ifexpr', 'ifexist', 'iferror', 'switch'])
|
|
8
|
+
const magicWords = new Set(['if', 'ifeq', 'ifexpr', 'ifexist', 'iferror', 'switch']), formattingTags = new Set([
|
|
9
|
+
'b',
|
|
10
|
+
'big',
|
|
11
|
+
'center',
|
|
12
|
+
'cite',
|
|
13
|
+
'code',
|
|
14
|
+
'del',
|
|
15
|
+
'dfn',
|
|
16
|
+
'em',
|
|
17
|
+
'font',
|
|
18
|
+
'i',
|
|
19
|
+
'ins',
|
|
20
|
+
'kbd',
|
|
21
|
+
'mark',
|
|
22
|
+
'pre',
|
|
23
|
+
'q',
|
|
24
|
+
's',
|
|
25
|
+
'samp',
|
|
26
|
+
'small',
|
|
27
|
+
'strike',
|
|
28
|
+
'strong',
|
|
29
|
+
'sub',
|
|
30
|
+
'sup',
|
|
31
|
+
'tt',
|
|
32
|
+
'u',
|
|
33
|
+
'var',
|
|
34
|
+
]), obsoleteTags = new Set(['strike', 'big', 'center', 'font', 'tt']);
|
|
9
35
|
/**
|
|
10
36
|
* HTML标签
|
|
11
37
|
* @classdesc `{childNodes: [AttributesToken]}`
|
|
@@ -74,7 +100,7 @@ class HtmlToken extends index_1.Token {
|
|
|
74
100
|
const { message: errorMsg } = e;
|
|
75
101
|
refError ??= (0, lint_1.generateForSelf)(this, { start }, '');
|
|
76
102
|
const [msg] = errorMsg.split(':'), error = { ...refError, message: Parser.msg(msg) };
|
|
77
|
-
if (msg === 'unclosed tag') {
|
|
103
|
+
if (msg === 'unclosed tag' && !formattingTags.has(this.name)) {
|
|
78
104
|
error.severity = 'warning';
|
|
79
105
|
}
|
|
80
106
|
else if (msg === 'unmatched closing tag') {
|
|
@@ -86,13 +112,21 @@ class HtmlToken extends index_1.Token {
|
|
|
86
112
|
errors.push(error);
|
|
87
113
|
}
|
|
88
114
|
}
|
|
115
|
+
if (obsoleteTags.has(this.name)) {
|
|
116
|
+
refError ??= (0, lint_1.generateForSelf)(this, { start }, '');
|
|
117
|
+
errors.push({
|
|
118
|
+
...refError,
|
|
119
|
+
message: Parser.msg('obsolete HTML tag'),
|
|
120
|
+
severity: 'warning',
|
|
121
|
+
});
|
|
122
|
+
}
|
|
89
123
|
return errors;
|
|
90
124
|
}
|
|
91
125
|
/**
|
|
92
126
|
* 搜索匹配的标签
|
|
93
127
|
* @throws `SyntaxError` 同时闭合和自封闭的标签
|
|
94
128
|
* @throws `SyntaxError` 无效自封闭标签
|
|
95
|
-
* @throws `SyntaxError`
|
|
129
|
+
* @throws `SyntaxError` 未匹配的标签
|
|
96
130
|
*/
|
|
97
131
|
findMatchingTag() {
|
|
98
132
|
const { html } = this.getAttribute('config'), { name: tagName, parentNode, closing } = this, string = (0, string_1.noWrap)(String(this));
|
|
@@ -16,6 +16,8 @@ function validate(key, val, config = Parser.getConfig(), halfParsed = false) {
|
|
|
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;
|
|
19
21
|
const regex = new RegExp(`^(?:(?:${config.protocol}|//)${string_1.extUrlCharFirst}|\0\\d+m\x7F)${string_1.extUrlChar}$`, 'iu');
|
|
20
22
|
if (regex.test(value)) {
|
|
21
23
|
return val;
|
package/dist/src/imagemap.js
CHANGED
|
@@ -59,7 +59,7 @@ class ImagemapToken extends index_1.Token {
|
|
|
59
59
|
}
|
|
60
60
|
else if (protocols.has(substr.slice(1, substr.indexOf(':') + 1))
|
|
61
61
|
|| protocols.has(substr.slice(1, substr.indexOf('//') + 2))) {
|
|
62
|
-
const mtEx = /^\[([^\]\s]+)(?:(\s+)(
|
|
62
|
+
const mtEx = /^\[([^\]\s]+)(?:(\s+(?=\S))([^\]]*))?\][\w\s]*$/u
|
|
63
63
|
.exec(substr);
|
|
64
64
|
if (mtEx) {
|
|
65
65
|
super.insertAt(new imagemapLink_1.ImagemapLinkToken(line.slice(0, i), mtEx.slice(1), substr.slice(substr.indexOf(']') + 1), config, accum));
|
package/dist/src/link/base.js
CHANGED
|
@@ -92,6 +92,10 @@ class LinkBaseToken extends index_1.Token {
|
|
|
92
92
|
rect ??= { start, ...this.getRootNode().posFromIndex(start) };
|
|
93
93
|
errors.push((0, lint_1.generateForChild)(target, rect, 'useless fragment'));
|
|
94
94
|
}
|
|
95
|
+
if (linkType === 'link' && this.closest('ext-link-text')) {
|
|
96
|
+
rect ??= { start, ...this.getRootNode().posFromIndex(start) };
|
|
97
|
+
errors.push((0, lint_1.generateForSelf)(this, rect, 'internal link in an external link'));
|
|
98
|
+
}
|
|
95
99
|
return errors;
|
|
96
100
|
}
|
|
97
101
|
/** @private */
|
|
@@ -3,6 +3,10 @@ import type { LintError } from '../../base';
|
|
|
3
3
|
/** `''`和`'''` */
|
|
4
4
|
export declare class QuoteToken extends NowikiBaseToken {
|
|
5
5
|
readonly type = "quote";
|
|
6
|
+
/** 是否粗体 */
|
|
7
|
+
get bold(): boolean;
|
|
8
|
+
/** 是否斜体 */
|
|
9
|
+
get italic(): boolean;
|
|
6
10
|
/** @override */
|
|
7
11
|
lint(start?: number): LintError[];
|
|
8
12
|
}
|
package/dist/src/nowiki/quote.js
CHANGED
|
@@ -8,6 +8,14 @@ const base_1 = require("./base");
|
|
|
8
8
|
// @ts-expect-error not implementing all abstract methods
|
|
9
9
|
class QuoteToken extends base_1.NowikiBaseToken {
|
|
10
10
|
type = 'quote';
|
|
11
|
+
/** 是否粗体 */
|
|
12
|
+
get bold() {
|
|
13
|
+
return this.innerText.length !== 2;
|
|
14
|
+
}
|
|
15
|
+
/** 是否斜体 */
|
|
16
|
+
get italic() {
|
|
17
|
+
return this.innerText.length !== 3;
|
|
18
|
+
}
|
|
11
19
|
/** @override */
|
|
12
20
|
lint(start = this.getAbsoluteIndex()) {
|
|
13
21
|
const { previousSibling, nextSibling } = this, message = Parser.msg('lonely "$1"', `'`), errors = [];
|
package/dist/src/parameter.js
CHANGED
|
@@ -59,6 +59,8 @@ class ParameterToken extends index_1.Token {
|
|
|
59
59
|
}
|
|
60
60
|
/** @override */
|
|
61
61
|
lint(start = this.getAbsoluteIndex()) {
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
63
|
+
/https?:\/\/(?:\[[\da-f:.]+\]|[^[\]<>"\t\n\p{Zs}])(?:[^[\]<>"\0\t\n\p{Zs}]|\0\d+c\x7F)*$/iu;
|
|
62
64
|
const errors = super.lint(start), { firstChild } = this, link = new RegExp(`https?://${string_1.extUrlCharFirst}${string_1.extUrlChar}$`, 'iu')
|
|
63
65
|
.exec(firstChild.toString(new Set(['comment', 'noinclude', 'include'])))?.[0];
|
|
64
66
|
if (link && new URL(link).search) {
|
|
@@ -9,6 +9,7 @@ import type { LintError } from '../../base';
|
|
|
9
9
|
*/
|
|
10
10
|
export declare class ExtToken extends TagPairToken {
|
|
11
11
|
readonly type = "ext";
|
|
12
|
+
closed: true;
|
|
12
13
|
readonly childNodes: [AttributesToken, Token];
|
|
13
14
|
abstract get firstChild(): AttributesToken;
|
|
14
15
|
abstract get lastChild(): Token;
|
|
@@ -6,11 +6,11 @@ export declare abstract class TagPairToken extends Token {
|
|
|
6
6
|
#private;
|
|
7
7
|
type: 'ext' | 'include';
|
|
8
8
|
readonly name: string;
|
|
9
|
+
closed: boolean;
|
|
10
|
+
selfClosing: boolean;
|
|
9
11
|
readonly childNodes: [AstNodes, AstNodes];
|
|
10
12
|
abstract get firstChild(): AstNodes;
|
|
11
13
|
abstract get lastChild(): AstNodes;
|
|
12
|
-
/** 是否闭合 */
|
|
13
|
-
get closed(): boolean;
|
|
14
14
|
/**
|
|
15
15
|
* @param name 标签名
|
|
16
16
|
* @param attr 标签属性
|
|
@@ -5,13 +5,9 @@ const Parser = require("../../index");
|
|
|
5
5
|
const index_1 = require("../index");
|
|
6
6
|
/** 成对标签 */
|
|
7
7
|
class TagPairToken extends index_1.Token {
|
|
8
|
-
#selfClosing;
|
|
9
|
-
#closed;
|
|
10
8
|
#tags;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return this.#closed;
|
|
14
|
-
}
|
|
9
|
+
closed;
|
|
10
|
+
selfClosing;
|
|
15
11
|
/**
|
|
16
12
|
* @param name 标签名
|
|
17
13
|
* @param attr 标签属性
|
|
@@ -22,23 +18,23 @@ class TagPairToken extends index_1.Token {
|
|
|
22
18
|
super(undefined, config);
|
|
23
19
|
this.setAttribute('name', name.toLowerCase());
|
|
24
20
|
this.#tags = [name, closed || name];
|
|
25
|
-
this
|
|
26
|
-
this
|
|
21
|
+
this.closed = closed !== '';
|
|
22
|
+
this.selfClosing = closed === undefined;
|
|
27
23
|
this.append(attr, inner);
|
|
28
24
|
const index = typeof attr === 'string' ? -1 : accum.indexOf(attr);
|
|
29
25
|
accum.splice(index === -1 ? Infinity : index, 0, this);
|
|
30
26
|
}
|
|
31
27
|
/** @private */
|
|
32
28
|
toString(omit) {
|
|
33
|
-
const { firstChild, lastChild, } = this, [opening, closing] = this.#tags;
|
|
34
|
-
return
|
|
29
|
+
const { selfClosing, firstChild, lastChild, } = this, [opening, closing] = this.#tags;
|
|
30
|
+
return selfClosing
|
|
35
31
|
? `<${opening}${firstChild.toString(omit)}/>`
|
|
36
32
|
: `<${opening}${firstChild.toString(omit)}>${lastChild.toString(omit)}${this.closed ? `</${closing}>` : ''}`;
|
|
37
33
|
}
|
|
38
34
|
/** @override */
|
|
39
35
|
text() {
|
|
40
36
|
const [opening, closing] = this.#tags;
|
|
41
|
-
return this
|
|
37
|
+
return this.selfClosing
|
|
42
38
|
? `<${opening}${this.firstChild.text()}/>`
|
|
43
39
|
: `<${opening}${super.text('>')}${this.closed ? `</${closing}>` : ''}`;
|
|
44
40
|
}
|
package/dist/src/transclude.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type { LintError } from '../base';
|
|
|
11
11
|
export declare class TranscludeToken extends Token {
|
|
12
12
|
#private;
|
|
13
13
|
type: 'template' | 'magic-word';
|
|
14
|
-
readonly modifier
|
|
14
|
+
readonly modifier: string;
|
|
15
15
|
readonly childNodes: [AtomToken | SyntaxToken, ...ParameterToken[]] | [SyntaxToken, AtomToken, AtomToken, ...ParameterToken[]];
|
|
16
16
|
abstract get firstChild(): AtomToken | SyntaxToken;
|
|
17
17
|
abstract get lastChild(): AtomToken | SyntaxToken | ParameterToken;
|
package/dist/src/transclude.js
CHANGED
|
@@ -70,9 +70,9 @@ class TranscludeToken extends index_1.Token {
|
|
|
70
70
|
}
|
|
71
71
|
if (this.type === 'template') {
|
|
72
72
|
const name = (0, string_1.removeComment)((0, string_1.decodeHtml)(title)).split('#')[0].trim();
|
|
73
|
-
if (!name ||
|
|
73
|
+
if (!name || /^:[\s_]*:|\0\d+[eh!+-]\x7F|[<>[\]{}\n]|%[\da-f]{2}/iu.test(name)) {
|
|
74
74
|
accum.pop();
|
|
75
|
-
throw new SyntaxError(
|
|
75
|
+
throw new SyntaxError('非法的模板名称');
|
|
76
76
|
}
|
|
77
77
|
const token = new atom_1.AtomToken(title, 'template-name', config, accum, {});
|
|
78
78
|
super.insertAt(token);
|
|
@@ -118,7 +118,7 @@ class TranscludeToken extends index_1.Token {
|
|
|
118
118
|
/** 获取模板或模块名 */
|
|
119
119
|
#getTitle() {
|
|
120
120
|
const isTemplate = this.type === 'template', child = this.childNodes[isTemplate ? 0 : 1];
|
|
121
|
-
return
|
|
121
|
+
return this.normalizeTitle(child.text(), isTemplate ? 10 : 828);
|
|
122
122
|
}
|
|
123
123
|
/** @private */
|
|
124
124
|
afterBuild() {
|
|
@@ -162,12 +162,7 @@ class TranscludeToken extends index_1.Token {
|
|
|
162
162
|
return errors;
|
|
163
163
|
}
|
|
164
164
|
const title = this.#getTitle();
|
|
165
|
-
if (
|
|
166
|
-
rect = { start, ...this.getRootNode().posFromIndex(start) };
|
|
167
|
-
errors.push((0, lint_1.generateForSelf)(this, rect, 'missing module name'));
|
|
168
|
-
return errors;
|
|
169
|
-
}
|
|
170
|
-
else if (title.fragment !== undefined) {
|
|
165
|
+
if (title.fragment !== undefined) {
|
|
171
166
|
rect = { start, ...this.getRootNode().posFromIndex(start) };
|
|
172
167
|
errors.push((0, lint_1.generateForChild)(childNodes[type === 'template' ? 0 : 1], rect, 'useless fragment'));
|
|
173
168
|
}
|
package/i18n/zh-hans.json
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"additional \"|\" in a table cell": "表格单元格中多余的\"|\"",
|
|
5
5
|
"additional \"|\" in the link text": "链接文本中多余的\"|\"",
|
|
6
6
|
"attributes of a closing tag": "位于闭合标签的属性",
|
|
7
|
+
"bold": "粗体单引号",
|
|
7
8
|
"conflicting image $1 parameter": "冲突的图片$1参数",
|
|
8
9
|
"containing invalid attribute": "包含无效属性",
|
|
9
10
|
"content to be moved out from the table": "将被移出表格的内容",
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
"illegal attribute name": "非法的属性名",
|
|
20
21
|
"illegal module name": "非法的模块名称",
|
|
21
22
|
"insecure style": "不安全的样式",
|
|
23
|
+
"internal link in an external link": "外链中的内链",
|
|
22
24
|
"invalid content in <$1>": "<$1>内的无效内容",
|
|
23
25
|
"invalid conversion flag": "无效的转换标记",
|
|
24
26
|
"invalid gallery image": "无效的图库图片",
|
|
@@ -27,17 +29,20 @@
|
|
|
27
29
|
"invalid parameter of $1": "$1的无效参数",
|
|
28
30
|
"invalid self-closing tag": "无效自封闭标签",
|
|
29
31
|
"invisible content inside triple brackets": "三重括号内的不可见部分",
|
|
32
|
+
"italic": "斜体单引号",
|
|
30
33
|
"lonely \"$1\"": "孤立的\"$1\"",
|
|
31
34
|
"missing module function": "缺少模块函数",
|
|
32
35
|
"missing module name": "缺少模块名称",
|
|
33
36
|
"nonzero tabindex": "不为0的tabindex",
|
|
34
37
|
"nothing should be in <$1>": "<$1>标签内不应有任何内容",
|
|
38
|
+
"obsolete attribute": "过时的属性",
|
|
39
|
+
"obsolete HTML tag": "过时的HTML标签",
|
|
35
40
|
"quotes": "引号",
|
|
36
41
|
"section header in a HTML tag": "HTML标签属性中的段落标题",
|
|
37
42
|
"table": "表格",
|
|
38
43
|
"tag that is both closing and self-closing": "同时闭合和自封闭的标签",
|
|
39
44
|
"template in an internal link target": "内链目标包含模板",
|
|
40
|
-
"unbalanced
|
|
45
|
+
"unbalanced $1 in a section header": "段落标题中不平衡的$1",
|
|
41
46
|
"unclosed $1": "未闭合的$1",
|
|
42
47
|
"unclosed tag": "未闭合的标签",
|
|
43
48
|
"unescaped query string in an anonymous parameter": "匿名参数中未转义的查询参数",
|
package/i18n/zh-hant.json
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"additional \"|\" in a table cell": "表哥單元格中多餘的\"|\"",
|
|
5
5
|
"additional \"|\" in the link text": "連結文本中多餘的\"|\"",
|
|
6
6
|
"attributes of a closing tag": "位於閉合標籤的屬性",
|
|
7
|
+
"bold apostrophes": "粗體撇號",
|
|
7
8
|
"conflicting image $1 parameter": "衝突的圖片$1參數",
|
|
8
9
|
"containing invalid attribute": "包含無效屬性",
|
|
9
10
|
"content to be moved out from the table": "將被移出表格的內容",
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
"illegal attribute name": "非法的屬性名",
|
|
20
21
|
"illegal module name": "非法的模組名稱",
|
|
21
22
|
"insecure style": "不安全的樣式",
|
|
23
|
+
"internal link in an external link": "外部連結中的內部連結",
|
|
22
24
|
"invalid content in <$1>": "<$1>內的無效內容",
|
|
23
25
|
"invalid conversion flag": "無效的轉換標記",
|
|
24
26
|
"invalid gallery image": "無效的圖庫圖片",
|
|
@@ -27,17 +29,20 @@
|
|
|
27
29
|
"invalid parameter of $1": "$1的無效參數",
|
|
28
30
|
"invalid self-closing tag": "無效自封閉標籤",
|
|
29
31
|
"invisible content inside triple brackets": "三重括號內的不可見部分",
|
|
32
|
+
"italic apostrophes": "斜體撇號",
|
|
30
33
|
"lonely \"$1\"": "孤立的\"$1\"",
|
|
31
34
|
"missing module function": "缺少模組函式",
|
|
32
35
|
"missing module name": "缺少模組名稱",
|
|
33
36
|
"nonzero tabindex": "不為0的tabindex",
|
|
34
37
|
"nothing should be in <$1>": "<$1>標籤內不應有任何內容",
|
|
38
|
+
"obsolete attribute": "過時的屬性",
|
|
39
|
+
"obsolete HTML tag": "過時的HTML標籤",
|
|
35
40
|
"quotes": "引號",
|
|
36
41
|
"section header in a HTML tag": "HTML標籤屬性中的段落標題",
|
|
37
42
|
"table": "表格",
|
|
38
43
|
"tag that is both closing and self-closing": "同時閉合和自封閉的標籤",
|
|
39
44
|
"template in an internal link target": "內部連結目標包含模板",
|
|
40
|
-
"unbalanced
|
|
45
|
+
"unbalanced $1 in a section header": "段落標題中不平衡的$1",
|
|
41
46
|
"unclosed $1": "未閉合的$1",
|
|
42
47
|
"unclosed tag": "未閉合的標籤",
|
|
43
48
|
"unescaped query string in an anonymous parameter": "匿名參數中未轉義的查詢參數",
|