wikiparser-node 1.13.9 → 1.14.1
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 +2 -0
- package/bundle/bundle.es7.js +37 -0
- package/bundle/bundle.min.js +30 -30
- package/dist/addon/transclude.js +10 -1
- package/dist/base.d.ts +1 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/lib/element.js +3 -1
- package/dist/lib/text.js +38 -27
- package/dist/lib/title.js +12 -9
- package/dist/parser/commentAndExt.js +2 -2
- package/dist/parser/externalLinks.js +2 -2
- package/dist/parser/html.js +2 -1
- package/dist/parser/links.js +3 -3
- package/dist/parser/magicLinks.js +10 -3
- package/dist/parser/selector.js +2 -2
- package/dist/src/arg.js +3 -11
- package/dist/src/attribute.js +8 -19
- package/dist/src/attributes.js +30 -12
- package/dist/src/converterFlags.js +1 -7
- package/dist/src/extLink.js +1 -0
- package/dist/src/gallery.js +2 -10
- package/dist/src/heading.js +41 -7
- package/dist/src/html.js +47 -27
- package/dist/src/imageParameter.js +6 -4
- package/dist/src/imagemap.js +8 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +12 -24
- package/dist/src/link/base.js +8 -5
- package/dist/src/link/file.js +5 -1
- package/dist/src/link/galleryImage.js +3 -1
- package/dist/src/magicLink.js +7 -21
- package/dist/src/nested.js +3 -11
- package/dist/src/nowiki/comment.js +1 -1
- package/dist/src/nowiki/quote.js +10 -8
- package/dist/src/paramTag/index.js +1 -7
- package/dist/src/parameter.js +1 -1
- package/dist/src/table/index.js +3 -1
- package/dist/src/tagPair/ext.js +3 -3
- package/dist/src/tagPair/include.js +2 -14
- package/dist/src/transclude.js +11 -3
- package/dist/util/sharable.d.mts +1 -0
- package/dist/util/sharable.mjs +168 -0
- package/dist/util/string.js +3 -2
- package/extensions/dist/base.js +3 -3
- package/extensions/es7/base.js +166 -0
- package/extensions/es7/lint.js +87 -0
- package/extensions/typings.d.ts +52 -0
- package/i18n/zh-hans.json +1 -1
- package/i18n/zh-hant.json +1 -1
- package/package.json +15 -11
package/dist/addon/transclude.js
CHANGED
|
@@ -94,7 +94,16 @@ transclude_1.TranscludeToken.prototype.fixDuplication =
|
|
|
94
94
|
if (args.length <= 1) {
|
|
95
95
|
continue;
|
|
96
96
|
}
|
|
97
|
-
const values = Map
|
|
97
|
+
const values = new Map();
|
|
98
|
+
for (const arg of args) {
|
|
99
|
+
const val = arg.getValue().trim();
|
|
100
|
+
if (values.has(val)) {
|
|
101
|
+
values.get(val).push(arg);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
values.set(val, [arg]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
98
107
|
let noMoreAnon = anonCount === 0 || !key.trim() || isNaN(key);
|
|
99
108
|
const emptyArgs = values.get('') ?? [], duplicatedArgs = [...values].filter(([val, { length }]) => val && length > 1).flatMap(([, curArgs]) => {
|
|
100
109
|
const anonIndex = noMoreAnon ? -1 : curArgs.findIndex(({ anon }) => anon);
|
package/dist/base.d.ts
CHANGED
|
@@ -63,9 +63,7 @@ export interface LintError {
|
|
|
63
63
|
endLine: number;
|
|
64
64
|
endCol: number;
|
|
65
65
|
fix?: LintError.Fix;
|
|
66
|
-
suggestions?:
|
|
67
|
-
desc: string;
|
|
68
|
-
})[];
|
|
66
|
+
suggestions?: LintError.Fix[];
|
|
69
67
|
}
|
|
70
68
|
export type AST = Record<string, string | number | boolean> & {
|
|
71
69
|
range: [number, number];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Config, LintError, TokenTypes, Parser as ParserBase, Stage } from './base';
|
|
1
|
+
import type { Config, LintError, TokenTypes, Parser as ParserBase, Stage, AST } from './base';
|
|
2
2
|
import type { Title } from './lib/title';
|
|
3
3
|
import type { Token } from './internal';
|
|
4
4
|
declare interface Parser extends ParserBase {
|
|
@@ -28,6 +28,6 @@ declare const Parser: Parser;
|
|
|
28
28
|
// @ts-expect-error mixed export styles
|
|
29
29
|
export = Parser;
|
|
30
30
|
export default Parser;
|
|
31
|
-
export type { Config, LintError, TokenTypes };
|
|
31
|
+
export type { Config, LintError, TokenTypes, AST, };
|
|
32
32
|
export type * from './internal';
|
|
33
33
|
declare global { type Acceptable = unknown; }
|
package/dist/index.js
CHANGED
|
@@ -222,8 +222,8 @@ const Parser = {
|
|
|
222
222
|
catch { }
|
|
223
223
|
}
|
|
224
224
|
for (const [name, filePath] of entries) {
|
|
225
|
-
if (name in globalThis) {
|
|
226
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
225
|
+
if (name in globalThis) { // eslint-disable-line es-x/no-global-this
|
|
226
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, es-x/no-global-this
|
|
227
227
|
Object.assign(globalThis, { [name]: require(filePath)[name] });
|
|
228
228
|
}
|
|
229
229
|
}
|
package/dist/lib/element.js
CHANGED
|
@@ -260,7 +260,9 @@ class AstElement extends node_1.AstNode {
|
|
|
260
260
|
print(opt = {}) {
|
|
261
261
|
const cl = opt.class;
|
|
262
262
|
return this.toString()
|
|
263
|
-
?
|
|
263
|
+
? (cl === '' ? '' : `<span class="wpb-${cl ?? this.type}">`)
|
|
264
|
+
+ (0, string_1.print)(this.childNodes, opt)
|
|
265
|
+
+ (cl === '' ? '' : '</span>')
|
|
264
266
|
: '';
|
|
265
267
|
}
|
|
266
268
|
/**
|
package/dist/lib/text.js
CHANGED
|
@@ -9,11 +9,11 @@ const constants_1 = require("../util/constants");
|
|
|
9
9
|
const debug_1 = require("../util/debug");
|
|
10
10
|
const html_1 = require("../util/html");
|
|
11
11
|
/* NOT FOR BROWSER END */
|
|
12
|
-
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
|
12
|
+
/* eslint-disable @typescript-eslint/no-unused-expressions, es-x/no-regexp-unicode-property-escapes */
|
|
13
13
|
/<\s*(?:\/\s*)?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*?\])|((?:^|\])[^[]*?)\]+|https?[:/]\/+/giu;
|
|
14
14
|
/^https?:\/\/(?:\[[\da-f:.]+\]|[^[\]<>"\t\n\p{Zs}])[^[\]<>"\t\n\p{Zs}]*\.(?:gif|png|jpg|jpeg)$/iu;
|
|
15
|
-
/* eslint-enable @typescript-eslint/no-unused-expressions */
|
|
16
|
-
const sp = String.raw `[
|
|
15
|
+
/* eslint-enable @typescript-eslint/no-unused-expressions, es-x/no-regexp-unicode-property-escapes */
|
|
16
|
+
const sp = String.raw `[${string_1.zs}\t]*`, source = String.raw `<\s*(?:/\s*)?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*?\])|((?:^|\])[^[]*?)\]+|(?:rfc|pmid)(?=[-::]?${sp}\d)|isbn(?=[-::]?${sp}(?:\d(?:${sp}|-)){6})`, errorSyntax = new RegExp(String.raw `${source}|https?[:/]/+`, 'giu'), errorSyntaxUrl = new RegExp(source, 'giu'), extImage = new RegExp(String.raw `^https?://${string_1.extUrlCharFirst}${string_1.extUrlChar}\.(?:gif|png|jpg|jpeg)$`, 'iu'), noLinkTypes = new Set(['attr-value', 'ext-link-text', 'link-text']), regexes = {
|
|
17
17
|
'[': /[[\]]/u,
|
|
18
18
|
'{': /[{}]/u,
|
|
19
19
|
']': /[[\]](?=[^[\]]*$)/u,
|
|
@@ -80,6 +80,14 @@ const sp = String.raw `[\p{Zs}\t]*`, source = String.raw `<\s*(?:/\s*)?([a-z]\w*
|
|
|
80
80
|
'param',
|
|
81
81
|
'xmp',
|
|
82
82
|
];
|
|
83
|
+
let wordRegex;
|
|
84
|
+
try {
|
|
85
|
+
// eslint-disable-next-line prefer-regex-literals, es-x/no-regexp-unicode-property-escapes
|
|
86
|
+
wordRegex = new RegExp(String.raw `[\p{L}\d_]`, 'u');
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
wordRegex = /\w/u;
|
|
90
|
+
}
|
|
83
91
|
/** 文本节点 */
|
|
84
92
|
class AstText extends node_1.AstNode {
|
|
85
93
|
data = '';
|
|
@@ -148,7 +156,16 @@ class AstText extends node_1.AstNode {
|
|
|
148
156
|
return [];
|
|
149
157
|
}
|
|
150
158
|
errorRegex.lastIndex = 0;
|
|
151
|
-
const errors = [], nextType = nextSibling?.type, nextName = nextSibling?.name, previousType = previousSibling?.type, root = this.getRootNode(), { ext, html } = root.getAttribute('config'), { top, left } = root.posFromIndex(start), tags = new Set([
|
|
159
|
+
const errors = [], nextType = nextSibling?.type, nextName = nextSibling?.name, previousType = previousSibling?.type, root = this.getRootNode(), rootStr = root.toString(), { ext, html } = root.getAttribute('config'), { top, left } = root.posFromIndex(start), tags = new Set([
|
|
160
|
+
'onlyinclude',
|
|
161
|
+
'noinclude',
|
|
162
|
+
'includeonly',
|
|
163
|
+
...ext,
|
|
164
|
+
...html[0],
|
|
165
|
+
...html[1],
|
|
166
|
+
...html[2],
|
|
167
|
+
...disallowedTags,
|
|
168
|
+
]);
|
|
152
169
|
for (let mt = errorRegex.exec(data); mt; mt = errorRegex.exec(data)) {
|
|
153
170
|
const [, tag, prefix] = mt;
|
|
154
171
|
let { index } = mt, error = mt[0].toLowerCase();
|
|
@@ -168,7 +185,7 @@ class AstText extends node_1.AstNode {
|
|
|
168
185
|
else if (char === ']' && (index || length > 1)) {
|
|
169
186
|
errorRegex.lastIndex--;
|
|
170
187
|
}
|
|
171
|
-
const startIndex = start + index, endIndex = startIndex + length,
|
|
188
|
+
const startIndex = start + index, endIndex = startIndex + length, nextChar = rootStr[endIndex], previousChar = rootStr[startIndex - 1], severity = length > 1 && !(char === '<' && !/[\s/>]/u.test(nextChar ?? '')
|
|
172
189
|
|| isHtmlAttrVal && (char === '[' || char === ']')
|
|
173
190
|
|| magicLink && type === 'parameter-value')
|
|
174
191
|
|| char === '{' && (nextChar === char || previousChar === '-')
|
|
@@ -213,39 +230,31 @@ class AstText extends node_1.AstNode {
|
|
|
213
230
|
endCol: startCol + length,
|
|
214
231
|
};
|
|
215
232
|
if (char === '<') {
|
|
216
|
-
e.suggestions = [
|
|
217
|
-
{
|
|
218
|
-
desc: 'escape',
|
|
219
|
-
range: [startIndex, startIndex + 1],
|
|
220
|
-
text: '<',
|
|
221
|
-
},
|
|
222
|
-
];
|
|
233
|
+
e.suggestions = [{ desc: 'escape', range: [startIndex, startIndex + 1], text: '<' }];
|
|
223
234
|
}
|
|
224
235
|
else if (char === 'h'
|
|
225
236
|
&& !(type === 'ext-link-text' || type === 'link-text')
|
|
226
|
-
&&
|
|
227
|
-
e.suggestions = [
|
|
228
|
-
{
|
|
229
|
-
desc: 'whitespace',
|
|
230
|
-
range: [startIndex, startIndex],
|
|
231
|
-
text: ' ',
|
|
232
|
-
},
|
|
233
|
-
];
|
|
237
|
+
&& wordRegex.test(previousChar || '')) {
|
|
238
|
+
e.suggestions = [{ desc: 'whitespace', range: [startIndex, startIndex], text: ' ' }];
|
|
234
239
|
}
|
|
235
240
|
else if (char === '[' && type === 'ext-link-text') {
|
|
236
241
|
const i = parentNode.getAbsoluteIndex() + parentNode.toString().length;
|
|
237
|
-
e.suggestions = [
|
|
238
|
-
{
|
|
239
|
-
desc: 'escape',
|
|
240
|
-
range: [i, i + 1],
|
|
241
|
-
text: ']',
|
|
242
|
-
},
|
|
243
|
-
];
|
|
242
|
+
e.suggestions = [{ desc: 'escape', range: [i, i + 1], text: ']' }];
|
|
244
243
|
}
|
|
245
244
|
else if (char === ']' && previousType === 'free-ext-link' && severity === 'error') {
|
|
246
245
|
const i = start - previousSibling.toString().length;
|
|
247
246
|
e.fix = { range: [i, i], text: '[', desc: 'left bracket' };
|
|
248
247
|
}
|
|
248
|
+
else if (magicLink) {
|
|
249
|
+
e.suggestions = [
|
|
250
|
+
...mt[0] === error
|
|
251
|
+
? []
|
|
252
|
+
: [{ desc: 'uppercase', range: [startIndex, endIndex], text: error }],
|
|
253
|
+
...nextChar === ':' || nextChar === ':'
|
|
254
|
+
? [{ desc: 'whitespace', range: [endIndex, endIndex + 1], text: ' ' }]
|
|
255
|
+
: [],
|
|
256
|
+
];
|
|
257
|
+
}
|
|
249
258
|
errors.push(e);
|
|
250
259
|
}
|
|
251
260
|
return errors;
|
|
@@ -378,6 +387,7 @@ class AstText extends node_1.AstNode {
|
|
|
378
387
|
}
|
|
379
388
|
}
|
|
380
389
|
else if (mt && nextSibling.type === 'category') {
|
|
390
|
+
// eslint-disable-next-line es-x/no-string-prototype-trimstart-trimend
|
|
381
391
|
const trimmed = this.data.trimEnd();
|
|
382
392
|
if (this.data !== trimmed) {
|
|
383
393
|
const { length } = trimmed;
|
|
@@ -398,6 +408,7 @@ class AstText extends node_1.AstNode {
|
|
|
398
408
|
}
|
|
399
409
|
}
|
|
400
410
|
else {
|
|
411
|
+
// eslint-disable-next-line es-x/no-string-prototype-trimstart-trimend
|
|
401
412
|
this.#setData(this.data.trimEnd());
|
|
402
413
|
}
|
|
403
414
|
for (const space of spaces) {
|
package/dist/lib/title.js
CHANGED
|
@@ -78,6 +78,7 @@ class Title {
|
|
|
78
78
|
}
|
|
79
79
|
catch { }
|
|
80
80
|
}
|
|
81
|
+
// eslint-disable-next-line es-x/no-string-prototype-trimstart-trimend
|
|
81
82
|
this.#fragment = (0, string_1.decodeHtml)(fragment).replace(/[_ ]+/gu, ' ').trimEnd().replaceAll(' ', '_');
|
|
82
83
|
}
|
|
83
84
|
}
|
|
@@ -129,7 +130,7 @@ class Title {
|
|
|
129
130
|
}
|
|
130
131
|
const i = title.indexOf('#');
|
|
131
132
|
if (i !== -1) {
|
|
132
|
-
let fragment = title.slice(i
|
|
133
|
+
let fragment = title.slice(i).trim().slice(1);
|
|
133
134
|
if (fragment.includes('%')) {
|
|
134
135
|
try {
|
|
135
136
|
fragment = (0, string_1.rawurldecode)(fragment);
|
|
@@ -155,11 +156,12 @@ class Title {
|
|
|
155
156
|
}
|
|
156
157
|
/** @private */
|
|
157
158
|
toString(display) {
|
|
158
|
-
return
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
159
|
+
return (display ? this.title.replace(/_/gu, ' ') : this.title)
|
|
160
|
+
+ (this.#fragment === undefined
|
|
161
|
+
&& this.#redirectFragment === undefined
|
|
162
|
+
? ''
|
|
163
|
+
: `#${this.#fragment
|
|
164
|
+
?? this.#redirectFragment}`);
|
|
163
165
|
}
|
|
164
166
|
/** 检测是否是重定向 */
|
|
165
167
|
getRedirection() {
|
|
@@ -236,9 +238,10 @@ class Title {
|
|
|
236
238
|
getUrl() {
|
|
237
239
|
const { title, fragment } = this;
|
|
238
240
|
if (title) {
|
|
239
|
-
return this.#path.replace('$1',
|
|
240
|
-
|
|
241
|
-
|
|
241
|
+
return this.#path.replace('$1', encodeURIComponent(title)
|
|
242
|
+
+ (fragment === undefined && this.#redirectFragment === undefined
|
|
243
|
+
? ''
|
|
244
|
+
: `#${encodeURIComponent(fragment ?? this.#redirectFragment)}`));
|
|
242
245
|
}
|
|
243
246
|
return fragment === undefined ? '' : `#${encodeURIComponent(fragment)}`;
|
|
244
247
|
}
|
|
@@ -56,10 +56,10 @@ const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
59
|
-
|
|
59
|
+
/<!--[\s\S]*?(?:-->|$)|<foo(?:\s[^>]*)?\/?>|<\/foo\s*>|<(bar)(\s[^>]*?)?(?:\/>|>([\s\S]*?)<\/(\1\s*)>)|<(baz)(\s[^>]*?)?(?:\/>|>([\s\S]*?)(?:<\/(baz\s*)>|$))/giu;
|
|
60
60
|
const ext = config.ext.join('|'), noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly',
|
|
61
61
|
/** Never cached due to the possibility of nested extension tags */
|
|
62
|
-
regex = new RegExp(String.raw
|
|
62
|
+
regex = new RegExp(String.raw `<!--[\s\S]*?(?:-->|$)|<${noincludeRegex}(?:\s[^>]*)?/?>|</${noincludeRegex}\s*>|<(${ext})(\s[^>]*?)?(?:/>|>([\s\S]*?)</(\1\s*)>)|<(${includeRegex})(\s[^>]*?)?(?:/>|>([\s\S]*?)(?:</(${includeRegex}\s*)>|$))`, 'giu');
|
|
63
63
|
return wikitext.replace(regex, (substr, name, attr, inner, closing, include, includeAttr, includeInner, includeClosing) => {
|
|
64
64
|
const l = accum.length;
|
|
65
65
|
let ch = 'n';
|
|
@@ -15,9 +15,9 @@ const constants_1 = require("../util/constants");
|
|
|
15
15
|
* @param inFile 是否在图链中
|
|
16
16
|
*/
|
|
17
17
|
const parseExternalLinks = (wikitext, config, accum, inFile) => {
|
|
18
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions, es-x/no-regexp-unicode-property-escapes
|
|
19
19
|
/\[((?:ftp:\/\/|\/\/)(?:\[[\da-f:.]+\]|[^[\]<>"\t\n\p{Zs}])[^[\]<>"\t\n\p{Zs}]*(?=[[\]<>"\t\p{Zs}]|\0\d))(\p{Zs}*(?!\p{Zs}))([^\]\n]*)\]/giu;
|
|
20
|
-
config.regexExternalLinks ??= new RegExp(String.raw `\[(\0\d+f\x7F|(?:(?:${config.protocol}|//)${string_1.extUrlCharFirst}|\0\d+m\x7F)${string_1.extUrlChar}(?=[[\]<>"\t
|
|
20
|
+
config.regexExternalLinks ??= new RegExp(String.raw `\[(\0\d+f\x7F|(?:(?:${config.protocol}|//)${string_1.extUrlCharFirst}|\0\d+m\x7F)${string_1.extUrlChar}(?=[[\]<>"\t${string_1.zs}]|\0\d))([${string_1.zs}]*(?![${string_1.zs}]))([^\]\x01-\x08\x0A-\x1F\uFFFD]*)\]`, 'giu');
|
|
21
21
|
return wikitext.replace(config.regexExternalLinks, (_, url, space, text) => {
|
|
22
22
|
const { length } = accum, mt = /&[lg]t;/u.exec(url);
|
|
23
23
|
if (mt) {
|
package/dist/parser/html.js
CHANGED
|
@@ -14,7 +14,8 @@ const regex = /^(\/?)([a-z][^\s/>]*)((?:\s|\/(?!>))[^>]*?)?(\/?>)([^<]*)$/iu;
|
|
|
14
14
|
* @param accum
|
|
15
15
|
*/
|
|
16
16
|
const parseHtml = (wikitext, config, accum) => {
|
|
17
|
-
|
|
17
|
+
const { html } = config;
|
|
18
|
+
config.htmlElements ??= new Set([...html[0], ...html[1], ...html[2]]);
|
|
18
19
|
const bits = wikitext.split('<');
|
|
19
20
|
let text = bits.shift();
|
|
20
21
|
for (const x of bits) {
|
package/dist/parser/links.js
CHANGED
|
@@ -10,7 +10,7 @@ const category_1 = require("../src/link/category");
|
|
|
10
10
|
/* NOT FOR BROWSER */
|
|
11
11
|
const constants_1 = require("../util/constants");
|
|
12
12
|
/* NOT FOR BROWSER END */
|
|
13
|
-
const regexImg = /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(\||\0\d+!\x7F)(
|
|
13
|
+
const regexImg = /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(\||\0\d+!\x7F)([\s\S]*)$/u;
|
|
14
14
|
/**
|
|
15
15
|
* 解析内部链接
|
|
16
16
|
* @param wikitext
|
|
@@ -22,8 +22,8 @@ const parseLinks = (wikitext, config, accum) => {
|
|
|
22
22
|
/^\s*(?:ftp:\/\/|\/\/)/iu;
|
|
23
23
|
config.regexLinks ??= new RegExp(String.raw `^\s*(?:${config.protocol}|//)`, 'iu');
|
|
24
24
|
const regex = true // eslint-disable-line no-constant-condition, @typescript-eslint/no-unnecessary-condition
|
|
25
|
-
? /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(?:(\||\0\d+!\x7F)(
|
|
26
|
-
: /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(?:(\||\0\d+!\x7F)(
|
|
25
|
+
? /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(?:(\||\0\d+!\x7F)([\s\S]*?[^\]]))?\]\]([\s\S]*)$/u
|
|
26
|
+
: /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(?:(\||\0\d+!\x7F)([\s\S]*?[^\]])?)?\]\]([\s\S]*)$/u, bits = wikitext.split('[[');
|
|
27
27
|
let s = bits.shift();
|
|
28
28
|
for (let i = 0; i < bits.length; i++) {
|
|
29
29
|
let mightBeImg = false, link, delimiter, text, after;
|
|
@@ -6,7 +6,7 @@ const magicLink_1 = require("../src/magicLink");
|
|
|
6
6
|
/* NOT FOR BROWSER */
|
|
7
7
|
const constants_1 = require("../util/constants");
|
|
8
8
|
/* NOT FOR BROWSER END */
|
|
9
|
-
const space = String.raw `[
|
|
9
|
+
const space = String.raw `[${string_1.zs}\t]| |�*160;|�*a0;`, sp = `(?:${space})+`, spdash = `(?:${space}|-)`, magicLinkPattern = String.raw `(?:RFC|PMID)${sp}\d+\b|ISBN${sp}(?:97[89]${spdash}?)?(?:\d${spdash}?){9}[\dx]\b`;
|
|
10
10
|
/**
|
|
11
11
|
* 解析自由外链
|
|
12
12
|
* @param wikitext
|
|
@@ -14,9 +14,16 @@ const space = String.raw `[\p{Zs}\t]| |�*160;|�*a0;`, sp = `(?:${space
|
|
|
14
14
|
* @param accum
|
|
15
15
|
*/
|
|
16
16
|
const parseMagicLinks = (wikitext, config, accum) => {
|
|
17
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions, es-x/no-regexp-unicode-property-escapes
|
|
18
18
|
/(^|[^\p{L}\d_])(?:(?:ftp:\/\/|http:\/\/)((?:\[[\da-f:.]+\]|[^[\]<>"\t\n\p{Zs}])[^[\]<>"\0\t\n\p{Zs}]*)|(?:rfc|pmid)[\p{Zs}\t]+\d+\b|isbn[\p{Zs}\t]+(?:97[89][\p{Zs}\t-]?)?(?:\d[\p{Zs}\t-]?){9}[\dx]\b)/giu;
|
|
19
|
-
|
|
19
|
+
if (!config.regexMagicLinks) {
|
|
20
|
+
try {
|
|
21
|
+
config.regexMagicLinks = new RegExp(String.raw `(^|[^\p{L}\d_])(?:(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})|${magicLinkPattern})`, 'giu');
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
config.regexMagicLinks = new RegExp(String.raw `(^|\W)(?:(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})|${magicLinkPattern})`, 'giu');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
20
27
|
return wikitext.replace(config.regexMagicLinks, (m, lead, p1) => {
|
|
21
28
|
let url = lead ? m.slice(lead.length) : m;
|
|
22
29
|
if (p1) {
|
package/dist/parser/selector.js
CHANGED
|
@@ -161,7 +161,7 @@ const matches = (token, step, scope, has) => {
|
|
|
161
161
|
}
|
|
162
162
|
else if (selector.length === 4) { // 情形2:属性选择器
|
|
163
163
|
const [key, equal, val = '', i] = selector, isAttr = typeof token.hasAttr === 'function' && typeof token.getAttr === 'function';
|
|
164
|
-
if (!(key in token
|
|
164
|
+
if (!(key in token || isAttr && token.hasAttr(key))) {
|
|
165
165
|
return equal === '!=';
|
|
166
166
|
}
|
|
167
167
|
const v = toCase(val, i), thisVal = getAttr(token, key);
|
|
@@ -173,7 +173,7 @@ const matches = (token, step, scope, has) => {
|
|
|
173
173
|
return Boolean(thisVals?.[Symbol.iterator])
|
|
174
174
|
&& [...thisVals].some(w => typeof w === 'string' && toCase(w, i) === v);
|
|
175
175
|
}
|
|
176
|
-
else if (!primitives.has(typeof thisVal)
|
|
176
|
+
else if (!(primitives.has(typeof thisVal) || thisVal instanceof title_1.Title)) {
|
|
177
177
|
throw new RangeError(`The complex attribute ${key} cannot be used in a selector!`);
|
|
178
178
|
}
|
|
179
179
|
const stringVal = toCase(String(thisVal), i);
|
package/dist/src/arg.js
CHANGED
|
@@ -95,7 +95,7 @@ class ArgToken extends index_2.Token {
|
|
|
95
95
|
if (!this.getAttribute('include')) {
|
|
96
96
|
const e = (0, lint_1.generateForSelf)(this, { start }, 'no-arg', 'unexpected template argument');
|
|
97
97
|
if (argDefault) {
|
|
98
|
-
e.
|
|
98
|
+
e.suggestions = [{ range: [start, e.endIndex], text: argDefault.text(), desc: 'expand' }];
|
|
99
99
|
}
|
|
100
100
|
return [e];
|
|
101
101
|
}
|
|
@@ -110,16 +110,8 @@ class ArgToken extends index_2.Token {
|
|
|
110
110
|
e.startIndex--;
|
|
111
111
|
e.startCol--;
|
|
112
112
|
e.suggestions = [
|
|
113
|
-
{
|
|
114
|
-
|
|
115
|
-
range: [e.startIndex, e.endIndex],
|
|
116
|
-
text: '',
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
desc: 'escape',
|
|
120
|
-
range: [e.startIndex, e.startIndex + 1],
|
|
121
|
-
text: '{{!}}',
|
|
122
|
-
},
|
|
113
|
+
{ desc: 'remove', range: [e.startIndex, e.endIndex], text: '' },
|
|
114
|
+
{ desc: 'escape', range: [e.startIndex, e.startIndex + 1], text: '{{!}}' },
|
|
123
115
|
];
|
|
124
116
|
return e;
|
|
125
117
|
}));
|
package/dist/src/attribute.js
CHANGED
|
@@ -180,16 +180,10 @@ let AttributeToken = (() => {
|
|
|
180
180
|
const e = (0, lint_1.generateForChild)(lastChild, rect, 'unclosed-quote', index_1.default.msg('unclosed $1', 'quotes'), 'warning');
|
|
181
181
|
e.startIndex--;
|
|
182
182
|
e.startCol--;
|
|
183
|
-
|
|
184
|
-
if (lastChild.childNodes.some(({ type: t, data }) => t === 'text' && /\s/u.test(data))) {
|
|
185
|
-
e.suggestions = [fix];
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
e.fix = fix;
|
|
189
|
-
}
|
|
183
|
+
e.suggestions = [{ range: [e.endIndex, e.endIndex], text: this.#quotes[0], desc: 'close' }];
|
|
190
184
|
errors.push(e);
|
|
191
185
|
}
|
|
192
|
-
const attrs = sharable_1.extAttrs[tag], attrs2 = sharable_1.htmlAttrs[tag];
|
|
186
|
+
const attrs = sharable_1.extAttrs[tag], attrs2 = sharable_1.htmlAttrs[tag], { length } = this.toString();
|
|
193
187
|
if (!attrs?.has(name)
|
|
194
188
|
&& !attrs2?.has(name)
|
|
195
189
|
// 不是未定义的扩展标签或包含嵌入的HTML标签
|
|
@@ -197,7 +191,10 @@ let AttributeToken = (() => {
|
|
|
197
191
|
&& (type === 'ext-attr' && !attrs2
|
|
198
192
|
|| !/^(?:xmlns:[\w:.-]+|data-(?!ooui|mw|parsoid)[^:]*)$/u.test(name)
|
|
199
193
|
&& (tag === 'meta' || tag === 'link' || !sharable_1.commonHtmlAttrs.has(name)))) {
|
|
200
|
-
errors.push(
|
|
194
|
+
errors.push({
|
|
195
|
+
...(0, lint_1.generateForChild)(firstChild, rect, 'illegal-attr', 'illegal attribute name'),
|
|
196
|
+
suggestions: [{ desc: 'remove', range: [start, start + length], text: '' }],
|
|
197
|
+
});
|
|
201
198
|
}
|
|
202
199
|
else if (sharable_1.obsoleteAttrs[tag]?.has(name)) {
|
|
203
200
|
errors.push((0, lint_1.generateForChild)(firstChild, rect, 'obsolete-attr', 'obsolete attribute', 'warning'));
|
|
@@ -208,16 +205,8 @@ let AttributeToken = (() => {
|
|
|
208
205
|
else if (name === 'tabindex' && typeof value === 'string' && value !== '0') {
|
|
209
206
|
const e = (0, lint_1.generateForChild)(lastChild, rect, 'illegal-attr', 'nonzero tabindex');
|
|
210
207
|
e.suggestions = [
|
|
211
|
-
{
|
|
212
|
-
|
|
213
|
-
range: [start, e.endIndex],
|
|
214
|
-
text: '',
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
desc: '0 tabindex',
|
|
218
|
-
range: [e.startIndex, e.endIndex],
|
|
219
|
-
text: '0',
|
|
220
|
-
},
|
|
208
|
+
{ desc: 'remove', range: [start, start + length], text: '' },
|
|
209
|
+
{ desc: '0 tabindex', range: [e.startIndex, e.endIndex], text: '0' },
|
|
221
210
|
];
|
|
222
211
|
errors.push(e);
|
|
223
212
|
}
|
package/dist/src/attributes.js
CHANGED
|
@@ -23,6 +23,14 @@ const toAttributeType = (type) => type.slice(0, -1);
|
|
|
23
23
|
* @param type 属性类型
|
|
24
24
|
*/
|
|
25
25
|
const toDirty = (type) => `${toAttributeType(type)}-dirty`;
|
|
26
|
+
let wordRegex;
|
|
27
|
+
try {
|
|
28
|
+
// eslint-disable-next-line prefer-regex-literals, es-x/no-regexp-unicode-property-escapes
|
|
29
|
+
wordRegex = new RegExp(String.raw `[\p{L}\d]`, 'u');
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
wordRegex = /[^\W_]/u;
|
|
33
|
+
}
|
|
26
34
|
/**
|
|
27
35
|
* 扩展和HTML标签属性
|
|
28
36
|
* @classdesc `{childNodes: ...AtomToken|AttributeToken}`
|
|
@@ -104,7 +112,7 @@ class AttributesToken extends index_2.Token {
|
|
|
104
112
|
this.#type = type;
|
|
105
113
|
this.setAttribute('name', name);
|
|
106
114
|
if (attr) {
|
|
107
|
-
const regex = /([^\s/](?:(?!\0\d+~\x7F)[^\s/=])*)(?:((?:\s(?:\s|\0\d+[cn]\x7F)*)?(?:=|\0\d+~\x7F)(?:\s|\0\d+[cn]\x7F)*)(?:(["'])(
|
|
115
|
+
const regex = /([^\s/](?:(?!\0\d+~\x7F)[^\s/=])*)(?:((?:\s(?:\s|\0\d+[cn]\x7F)*)?(?:=|\0\d+~\x7F)(?:\s|\0\d+[cn]\x7F)*)(?:(["'])([\s\S]*?)(\3|$)|(\S*)))?/gu;
|
|
108
116
|
let out = '', mt = regex.exec(attr), lastIndex = 0;
|
|
109
117
|
const insertDirty = /** 插入无效属性 */ () => {
|
|
110
118
|
if (out) {
|
|
@@ -168,8 +176,11 @@ class AttributesToken extends index_2.Token {
|
|
|
168
176
|
lint(start = this.getAbsoluteIndex(), re) {
|
|
169
177
|
const errors = super.lint(start, re), { parentNode, childNodes } = this, attrs = new Map(), duplicated = new Set(), rect = new rect_1.BoundingRect(this, start);
|
|
170
178
|
if (parentNode?.type === 'html' && parentNode.closing && this.text().trim()) {
|
|
171
|
-
const e = (0, lint_1.generateForSelf)(this, rect, 'no-ignored', 'attributes of a closing tag');
|
|
172
|
-
e.
|
|
179
|
+
const e = (0, lint_1.generateForSelf)(this, rect, 'no-ignored', 'attributes of a closing tag'), index = parentNode.getAbsoluteIndex();
|
|
180
|
+
e.suggestions = [
|
|
181
|
+
{ desc: 'remove', range: [start, e.endIndex], text: '' },
|
|
182
|
+
{ desc: 'open', range: [index + 1, index + 2], text: '' },
|
|
183
|
+
];
|
|
173
184
|
errors.push(e);
|
|
174
185
|
}
|
|
175
186
|
for (const attr of childNodes) {
|
|
@@ -186,21 +197,28 @@ class AttributesToken extends index_2.Token {
|
|
|
186
197
|
else {
|
|
187
198
|
const str = attr.text().trim();
|
|
188
199
|
if (str) {
|
|
189
|
-
const e = (0, lint_1.generateForChild)(attr, rect, 'no-ignored', 'containing invalid attribute',
|
|
190
|
-
e.suggestions = [
|
|
191
|
-
{
|
|
192
|
-
desc: 'remove',
|
|
193
|
-
range: [e.startIndex, e.endIndex],
|
|
194
|
-
text: ' ',
|
|
195
|
-
},
|
|
196
|
-
];
|
|
200
|
+
const e = (0, lint_1.generateForChild)(attr, rect, 'no-ignored', 'containing invalid attribute', wordRegex.test(str) ? 'error' : 'warning');
|
|
201
|
+
e.suggestions = [{ desc: 'remove', range: [e.startIndex, e.endIndex], text: ' ' }];
|
|
197
202
|
errors.push(e);
|
|
198
203
|
}
|
|
199
204
|
}
|
|
200
205
|
}
|
|
201
206
|
if (duplicated.size > 0) {
|
|
202
207
|
for (const key of duplicated) {
|
|
203
|
-
|
|
208
|
+
const pairs = attrs.get(key).map(attr => {
|
|
209
|
+
const value = attr.getValue();
|
|
210
|
+
return [attr, value === true ? '' : value];
|
|
211
|
+
});
|
|
212
|
+
errors.push(...pairs.map(([attr, value], i) => {
|
|
213
|
+
const e = (0, lint_1.generateForChild)(attr, rect, 'no-duplicate', index_1.default.msg('duplicated $1 attribute', key)), remove = { desc: 'remove', range: [e.startIndex, e.endIndex], text: '' };
|
|
214
|
+
if (!value || pairs.slice(0, i).some(([, v]) => v === value)) {
|
|
215
|
+
e.fix = remove;
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
e.suggestions = [remove];
|
|
219
|
+
}
|
|
220
|
+
return e;
|
|
221
|
+
}));
|
|
204
222
|
}
|
|
205
223
|
}
|
|
206
224
|
return errors;
|
|
@@ -91,13 +91,7 @@ class ConverterFlagsToken extends index_2.Token {
|
|
|
91
91
|
e.fix = { range: [e.startIndex, e.endIndex], text: flag.toUpperCase(), desc: 'uppercase' };
|
|
92
92
|
}
|
|
93
93
|
else {
|
|
94
|
-
e.suggestions = [
|
|
95
|
-
{
|
|
96
|
-
desc: 'remove',
|
|
97
|
-
range: [e.startIndex - (i && 1), e.endIndex],
|
|
98
|
-
text: '',
|
|
99
|
-
},
|
|
100
|
-
];
|
|
94
|
+
e.suggestions = [{ desc: 'remove', range: [e.startIndex - (i && 1), e.endIndex], text: '' }];
|
|
101
95
|
}
|
|
102
96
|
errors.push(e);
|
|
103
97
|
}
|
package/dist/src/extLink.js
CHANGED
|
@@ -168,6 +168,7 @@ let ExtLinkToken = (() => {
|
|
|
168
168
|
&& length > 1
|
|
169
169
|
&& (firstChild?.type === 'text' || firstChild?.type === 'converter')
|
|
170
170
|
// 都替换成`<`肯定不对,但无妨
|
|
171
|
+
// eslint-disable-next-line es-x/no-regexp-unicode-property-escapes
|
|
171
172
|
&& /^[^[\]<>"\0-\x1F\x7F\p{Zs}\uFFFD]/u.test(lastChild.text().replace(/&[lg]t;/u, '<'))) {
|
|
172
173
|
this.#space = ' ';
|
|
173
174
|
}
|
package/dist/src/gallery.js
CHANGED
|
@@ -92,16 +92,8 @@ class GalleryToken extends index_2.Token {
|
|
|
92
92
|
startCol,
|
|
93
93
|
endCol: startCol + length,
|
|
94
94
|
suggestions: [
|
|
95
|
-
{
|
|
96
|
-
|
|
97
|
-
range: [start, endIndex],
|
|
98
|
-
text: '',
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
desc: 'comment',
|
|
102
|
-
range: [start, endIndex],
|
|
103
|
-
text: `<!--${str}-->`,
|
|
104
|
-
},
|
|
95
|
+
{ desc: 'remove', range: [start, endIndex], text: '' },
|
|
96
|
+
{ desc: 'comment', range: [start, endIndex], text: `<!--${str}-->` },
|
|
105
97
|
],
|
|
106
98
|
});
|
|
107
99
|
}
|
package/dist/src/heading.js
CHANGED
|
@@ -135,21 +135,55 @@ let HeadingToken = (() => {
|
|
|
135
135
|
}
|
|
136
136
|
/** @private */
|
|
137
137
|
lint(start = this.getAbsoluteIndex(), re) {
|
|
138
|
-
const errors = super.lint(start, re), { firstChild, level } = this, innerStr = firstChild.toString(), quotes = firstChild.childNodes.filter((0, debug_1.isToken)('quote')), boldQuotes = quotes.filter(({ bold }) => bold), italicQuotes = quotes.filter(({ italic }) => italic), rect = new rect_1.BoundingRect(this, start);
|
|
138
|
+
const errors = super.lint(start, re), { firstChild, level } = this, innerStr = firstChild.toString(), unbalancedStart = innerStr.startsWith('='), unbalanced = unbalancedStart || innerStr.endsWith('='), quotes = firstChild.childNodes.filter((0, debug_1.isToken)('quote')), boldQuotes = quotes.filter(({ bold }) => bold), italicQuotes = quotes.filter(({ italic }) => italic), rect = new rect_1.BoundingRect(this, start);
|
|
139
139
|
if (this.level === 1) {
|
|
140
|
-
|
|
140
|
+
const e = (0, lint_1.generateForChild)(firstChild, rect, 'h1', '<h1>');
|
|
141
|
+
if (!unbalanced) {
|
|
142
|
+
e.suggestions = [{ desc: 'h2', range: [e.startIndex, e.endIndex], text: `=${innerStr}=` }];
|
|
143
|
+
}
|
|
144
|
+
errors.push(e);
|
|
141
145
|
}
|
|
142
|
-
if (
|
|
143
|
-
|
|
146
|
+
if (unbalanced) {
|
|
147
|
+
const e = (0, lint_1.generateForChild)(firstChild, rect, 'unbalanced-header', index_1.default.msg('unbalanced $1 in a section header', '"="'));
|
|
148
|
+
if (innerStr === '=') {
|
|
149
|
+
//
|
|
150
|
+
}
|
|
151
|
+
else if (unbalancedStart) {
|
|
152
|
+
const [extra] = /^=+/u.exec(innerStr);
|
|
153
|
+
e.suggestions = [
|
|
154
|
+
{ desc: `h${level}`, range: [e.startIndex, e.startIndex + extra.length], text: '' },
|
|
155
|
+
{ desc: `h${level + extra.length}`, range: [e.endIndex, e.endIndex], text: extra },
|
|
156
|
+
];
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
const extra = /[^=](=+)$/u.exec(innerStr)[1];
|
|
160
|
+
e.suggestions = [
|
|
161
|
+
{ desc: `h${level}`, range: [e.endIndex - extra.length, e.endIndex], text: '' },
|
|
162
|
+
{ desc: `h${level + extra.length}`, range: [e.startIndex, e.startIndex], text: extra },
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
errors.push(e);
|
|
144
166
|
}
|
|
145
167
|
if (this.closest('html-attrs,table-attrs')) {
|
|
146
168
|
errors.push((0, lint_1.generateForSelf)(this, rect, 'parsing-order', 'section header in a HTML tag'));
|
|
147
169
|
}
|
|
170
|
+
const rootStr = this.getRootNode().toString();
|
|
148
171
|
if (boldQuotes.length % 2) {
|
|
149
|
-
|
|
172
|
+
const e = (0, lint_1.generateForChild)(boldQuotes[boldQuotes.length - 1], { ...rect, start: start + level, left: rect.left + level }, 'format-leakage', index_1.default.msg('unbalanced $1 in a section header', 'bold apostrophes')), end = start + level + innerStr.length;
|
|
173
|
+
if (rootStr.slice(e.endIndex, end).trim()) {
|
|
174
|
+
e.suggestions = [{ desc: 'close', range: [end, end], text: "'''" }];
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
e.fix = { desc: 'remove', range: [e.startIndex, e.endIndex], text: '' };
|
|
178
|
+
}
|
|
179
|
+
errors.push(e);
|
|
150
180
|
}
|
|
151
181
|
if (italicQuotes.length % 2) {
|
|
152
|
-
|
|
182
|
+
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;
|
|
183
|
+
e.fix = rootStr.slice(e.endIndex, end).trim()
|
|
184
|
+
? { desc: 'close', range: [end, end], text: "''" }
|
|
185
|
+
: { desc: 'remove', range: [e.startIndex, e.endIndex], text: '' };
|
|
186
|
+
errors.push(e);
|
|
153
187
|
}
|
|
154
188
|
return errors;
|
|
155
189
|
}
|
|
@@ -205,7 +239,7 @@ let HeadingToken = (() => {
|
|
|
205
239
|
if (headings?.has(lcId)) {
|
|
206
240
|
let i = 2;
|
|
207
241
|
for (; headings.has(`${lcId}_${i}`); i++) {
|
|
208
|
-
//
|
|
242
|
+
//
|
|
209
243
|
}
|
|
210
244
|
id = `${id}_${i}`;
|
|
211
245
|
headings.add(`${lcId}_${i}`);
|