wikilint 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/config/.schema.json +5 -0
- package/dist/base.d.mts +1 -0
- package/dist/base.d.ts +1 -0
- package/dist/bin/config.js +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +4 -4
- package/dist/lib/document.d.ts +3 -2
- package/dist/lib/document.js +3 -1
- package/dist/lib/lsp.js +4 -5
- package/dist/lib/text.js +5 -14
- package/dist/lib/title.js +6 -1
- package/dist/parser/commentAndExt.js +99 -60
- package/dist/parser/converter.js +2 -3
- package/dist/parser/externalLinks.js +11 -6
- package/dist/parser/magicLinks.js +5 -13
- package/dist/parser/table.js +2 -3
- package/dist/src/attribute.js +1 -1
- package/dist/src/attributes.js +3 -14
- package/dist/src/commented.js +9 -14
- package/dist/src/index.js +1 -47
- package/dist/src/link/file.js +2 -3
- package/dist/src/link/galleryImage.js +1 -1
- package/dist/src/multiLine/gallery.js +13 -4
- package/dist/src/nested.js +45 -7
- package/dist/src/parameter.js +3 -3
- package/dist/src/pre.js +8 -23
- package/dist/src/singleLine/charinsertLine.js +12 -12
- package/dist/src/tagPair/ext.js +2 -2
- package/dist/src/tagPair/translate.js +43 -13
- package/dist/util/replace.js +59 -0
- package/package.json +5 -4
package/README.md
CHANGED
package/config/.schema.json
CHANGED
|
@@ -247,6 +247,11 @@
|
|
|
247
247
|
"description": "base URL of the server",
|
|
248
248
|
"type": "string",
|
|
249
249
|
"pattern": "^(?:https?:)?//"
|
|
250
|
+
},
|
|
251
|
+
"capitalLinks": {
|
|
252
|
+
"description": "whether to capitalize the first letter of page titles",
|
|
253
|
+
"type": "boolean",
|
|
254
|
+
"default": true
|
|
250
255
|
}
|
|
251
256
|
},
|
|
252
257
|
"required": [
|
package/dist/base.d.mts
CHANGED
package/dist/base.d.ts
CHANGED
package/dist/bin/config.js
CHANGED
|
@@ -41,7 +41,7 @@ const filterGadget = (id) => {
|
|
|
41
41
|
const n = Number(id);
|
|
42
42
|
return n < 2300 || n > 2303; // Gadget, Gadget talk, Gadget definition, Gadget definition talk
|
|
43
43
|
};
|
|
44
|
-
const pkg = 'wikilint', version = '3.
|
|
44
|
+
const pkg = 'wikilint', version = '3.1.0';
|
|
45
45
|
/**
|
|
46
46
|
* Get the parser configuration for a Wikimedia Foundation project.
|
|
47
47
|
* @param site site nickname
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { LintConfiguration } from './lib/lintConfig';
|
|
2
|
-
import { Title } from './lib/title';
|
|
3
2
|
import type { MaxStage, Config, ConfigData, LintError, LintConfig, TokenTypes, Parser as ParserBase } from './base';
|
|
4
|
-
import type { TitleOptions } from './lib/title';
|
|
3
|
+
import type { TitleOptions, Title } from './lib/title';
|
|
5
4
|
import type { LanguageService, QuickFixData } from './lib/lsp';
|
|
6
5
|
import type { Token } from './internal';
|
|
7
6
|
declare interface Parser extends ParserBase {
|
package/dist/index.js
CHANGED
|
@@ -9,14 +9,13 @@ const debug_1 = require("./util/debug");
|
|
|
9
9
|
const constants_1 = require("./util/constants");
|
|
10
10
|
const string_1 = require("./util/string");
|
|
11
11
|
const lintConfig_1 = require("./lib/lintConfig");
|
|
12
|
-
const title_1 = require("./lib/title");
|
|
13
12
|
/* NOT FOR BROWSER ONLY */
|
|
14
13
|
const fs_1 = __importDefault(require("fs"));
|
|
15
14
|
const path_1 = __importDefault(require("path"));
|
|
16
15
|
const common_1 = require("@bhsd/common");
|
|
17
16
|
const diff_1 = require("./util/diff");
|
|
18
17
|
/* NOT FOR BROWSER ONLY */
|
|
19
|
-
const re = new RegExp(String.raw `^https
|
|
18
|
+
const re = new RegExp(String.raw `^https?://([^./]+)\.(${common_1.wmf})\.org`, 'iu');
|
|
20
19
|
/**
|
|
21
20
|
* require一个JSON文件
|
|
22
21
|
* @param file 文件名
|
|
@@ -139,9 +138,10 @@ const Parser = {
|
|
|
139
138
|
},
|
|
140
139
|
/** @implements */
|
|
141
140
|
normalizeTitle(title, defaultNs = 0, include, config = Parser.getConfig(), opt, accum = []) {
|
|
141
|
+
const { Title: TitleObj } = require('./lib/title');
|
|
142
142
|
let titleObj;
|
|
143
143
|
if (opt?.halfParsed) {
|
|
144
|
-
titleObj = new
|
|
144
|
+
titleObj = new TitleObj(title, defaultNs, config, opt, accum);
|
|
145
145
|
}
|
|
146
146
|
else {
|
|
147
147
|
const { Token } = require('./src/index');
|
|
@@ -150,7 +150,7 @@ const Parser = {
|
|
|
150
150
|
root.type = 'root';
|
|
151
151
|
root.pageName = opt?.page;
|
|
152
152
|
root.parseOnce(0, include).parseOnce();
|
|
153
|
-
const t = new
|
|
153
|
+
const t = new TitleObj(root.firstChild.toString(), defaultNs, config, opt, accum);
|
|
154
154
|
root.build();
|
|
155
155
|
const keys = [
|
|
156
156
|
'main',
|
package/dist/lib/document.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { TextDocument, Range } from 'vscode-languageserver-textdocument';
|
|
|
3
3
|
import type * as JsonLanguageService from 'vscode-json-languageservice';
|
|
4
4
|
import type * as CssLanguageService from 'vscode-css-languageservice';
|
|
5
5
|
import type * as HtmlLanguageService from 'vscode-html-languageservice';
|
|
6
|
-
import type {
|
|
6
|
+
import type { Warning } from 'stylelint';
|
|
7
7
|
import type { Token } from '../internal';
|
|
8
8
|
export interface TexvcLocation {
|
|
9
9
|
offset: number;
|
|
@@ -31,11 +31,12 @@ declare interface Texvcjs {
|
|
|
31
31
|
usemhchem?: boolean;
|
|
32
32
|
}): TexvcReport;
|
|
33
33
|
}
|
|
34
|
+
declare type StylelintUtil = (code: string) => Promise<Warning[]>;
|
|
34
35
|
export declare const loadTexvcjs: () => Texvcjs | null;
|
|
35
36
|
export declare const loadJsonLSP: () => JsonLanguageService.LanguageService | null;
|
|
36
37
|
export declare const loadCssLSP: () => CssLanguageService.LanguageService | null;
|
|
37
38
|
export declare const loadHtmlData: () => HtmlLanguageService.IHTMLDataProvider | null;
|
|
38
|
-
export declare const loadStylelint: () =>
|
|
39
|
+
export declare const loadStylelint: () => StylelintUtil | null;
|
|
39
40
|
/** embedded document */
|
|
40
41
|
declare abstract class EmbeddedDocument implements TextDocument {
|
|
41
42
|
#private;
|
package/dist/lib/document.js
CHANGED
|
@@ -96,7 +96,9 @@ const loadStylelint = () => {
|
|
|
96
96
|
NPM: {
|
|
97
97
|
if (stylelint === undefined) {
|
|
98
98
|
try {
|
|
99
|
-
stylelint = require('stylelint');
|
|
99
|
+
const stylelintApi = require('stylelint'), { styleLint } = require('@bhsd/stylelint-util');
|
|
100
|
+
const cssRules = { 'block-no-empty': null };
|
|
101
|
+
stylelint = /** @implements */ (code) => styleLint(stylelintApi, code, cssRules);
|
|
100
102
|
}
|
|
101
103
|
catch /* c8 ignore start */ {
|
|
102
104
|
stylelint = null;
|
package/dist/lib/lsp.js
CHANGED
|
@@ -20,7 +20,6 @@ const path_1 = __importDefault(require("path"));
|
|
|
20
20
|
const util_1 = __importDefault(require("util"));
|
|
21
21
|
const child_process_1 = require("child_process");
|
|
22
22
|
const crypto_1 = require("crypto");
|
|
23
|
-
const stylelint_util_1 = require("@bhsd/stylelint-util");
|
|
24
23
|
const color_1 = require("@bhsd/common/color");
|
|
25
24
|
const search_1 = __importDefault(require("../vendor/search"));
|
|
26
25
|
const constants_2 = require("../util/constants");
|
|
@@ -298,7 +297,7 @@ const partialParse = async (wikitext, watch, include, config = index_1.default.g
|
|
|
298
297
|
};
|
|
299
298
|
/* NOT FOR BROWSER ONLY */
|
|
300
299
|
/** @see https://www.npmjs.com/package/stylelint-config-recommended */
|
|
301
|
-
const
|
|
300
|
+
const colors = Object.keys(color_1.colorsNamed), sources = { 'invalid-css': 'css', 'invalid-math': 'texvc' }, jsonSelector = constants_2.jsonTags.map(s => `ext#${s}`).join(), scores = new Map();
|
|
302
301
|
/**
|
|
303
302
|
* Correct the position of an error.
|
|
304
303
|
* @param height
|
|
@@ -586,7 +585,7 @@ class LanguageService {
|
|
|
586
585
|
this.config ??= index_1.default.getConfig();
|
|
587
586
|
const { nsid, ext, html, parserFunction: [insensitive, sensitive, ...other], doubleUnderscore, protocol, img, } = this.config, tags = new Set([ext, html].flat(2));
|
|
588
587
|
const re = new RegExp('(?:' // eslint-disable-line prefer-template
|
|
589
|
-
+ String.raw `<(
|
|
588
|
+
+ String.raw `<(/?\w*)` // tag ($1)
|
|
590
589
|
+ '|'
|
|
591
590
|
+ String.raw `(\{{2,4}|\[\[)\s*([^|{}<>[\]\s][^|{}<>[\]#]*)?` // braces and brackets ($2, $3)
|
|
592
591
|
+ '|'
|
|
@@ -628,7 +627,7 @@ class LanguageService {
|
|
|
628
627
|
* @param position position / 位置
|
|
629
628
|
*/
|
|
630
629
|
async provideCompletionItems(text, position) {
|
|
631
|
-
const { re, allTags, functions, switches, jaSwitches, protocols, params, tags, ext, } = this.#prepareCompletionConfig(), { line, character } = position, curLine = text.split(/\r?\n/u, line + 1)[line], mt = re.exec(curLine?.slice(0, character) ?? '');
|
|
630
|
+
const { re, allTags, functions, switches, jaSwitches, protocols, params, tags, ext, } = this.#prepareCompletionConfig(), { line, character } = position, curLine = text.split(/\r?\n/u, line + 1)[line], mt = re.exec(curLine?.slice(Math.max(0, character - 1e4), character) ?? '');
|
|
632
631
|
if (mt?.[1] !== undefined) { // tag
|
|
633
632
|
const closing = mt[1].startsWith('/');
|
|
634
633
|
return getCompletion(allTags, 'Class', mt[1].slice(closing ? 1 : 0), position, closing && !curLine?.slice(character).trimStart().startsWith('>') ? '>' : '');
|
|
@@ -880,7 +879,7 @@ class LanguageService {
|
|
|
880
879
|
if (tokens.length === 0) {
|
|
881
880
|
return [];
|
|
882
881
|
}
|
|
883
|
-
const code = tokens.map(({ type, tag, lastChild }, i) => `${type === 'ext-attr' ? 'div' : tag}#${i}{\n${(0, common_1.sanitizeInlineStyle)(lastChild.toString())}\n}`).join('\n'), cssErrors = await (
|
|
882
|
+
const code = tokens.map(({ type, tag, lastChild }, i) => `${type === 'ext-attr' ? 'div' : tag}#${i}{\n${(0, common_1.sanitizeInlineStyle)(lastChild.toString())}\n}`).join('\n'), cssErrors = await stylelint(code);
|
|
884
883
|
if (cssErrors.length === 0) {
|
|
885
884
|
return [];
|
|
886
885
|
}
|
package/dist/lib/text.js
CHANGED
|
@@ -43,16 +43,6 @@ const errorSyntaxUrl = /* #__PURE__ */ new RegExp(source, 'giu'), noLinkTypes =
|
|
|
43
43
|
'select',
|
|
44
44
|
'textarea',
|
|
45
45
|
]);
|
|
46
|
-
const wordRegex = /* #__PURE__ */ (() => {
|
|
47
|
-
try {
|
|
48
|
-
// eslint-disable-next-line prefer-regex-literals
|
|
49
|
-
return new RegExp(String.raw `[\p{L}\p{N}_]`, 'u');
|
|
50
|
-
}
|
|
51
|
-
catch /* c8 ignore start */ {
|
|
52
|
-
return /\w/u;
|
|
53
|
-
}
|
|
54
|
-
/* c8 ignore stop */
|
|
55
|
-
})();
|
|
56
46
|
/**
|
|
57
47
|
* text node
|
|
58
48
|
*
|
|
@@ -105,7 +95,8 @@ class AstText extends node_1.AstNode {
|
|
|
105
95
|
return [];
|
|
106
96
|
}
|
|
107
97
|
errorRegex.lastIndex = 0;
|
|
108
|
-
|
|
98
|
+
let rule = 'tag-like';
|
|
99
|
+
const errors = [], nextType = nextSibling?.type, nextName = nextSibling?.name, previousType = previousSibling?.type, root = this.getRootNode(), rootStr = root.toString(), { ext, html, variants } = root.getAttribute('config'), { top, left } = root.posFromIndex(start), { lintConfig } = index_1.default, tagLike = lintConfig.rules[rule], specified = tagLike[1]
|
|
109
100
|
? new Set(Object.keys(tagLike[1]).filter(tag => tag !== 'invalid' && tag !== 'disallowed'))
|
|
110
101
|
: new Set(), tags = new Set([
|
|
111
102
|
'onlyinclude',
|
|
@@ -116,7 +107,7 @@ class AstText extends node_1.AstNode {
|
|
|
116
107
|
...html[1],
|
|
117
108
|
...html[2],
|
|
118
109
|
...specified,
|
|
119
|
-
...lintConfig.getSeverity(
|
|
110
|
+
...lintConfig.getSeverity(rule, 'disallowed') ? disallowedTags : [],
|
|
120
111
|
]);
|
|
121
112
|
for (let mt = errorRegex.exec(data); mt; mt = errorRegex.exec(data)) {
|
|
122
113
|
let { index, 0: error } = mt;
|
|
@@ -143,7 +134,7 @@ class AstText extends node_1.AstNode {
|
|
|
143
134
|
errorRegex.lastIndex--;
|
|
144
135
|
}
|
|
145
136
|
// Rule & Severity
|
|
146
|
-
let startIndex = start + index, endIndex = startIndex + length,
|
|
137
|
+
let startIndex = start + index, endIndex = startIndex + length, severity;
|
|
147
138
|
const nextChar = rootStr[endIndex], previousChar = rootStr[startIndex - 1], leftBracket = lbrace || lbrack, lConverter = error === '{' && previousChar === '-' && variants.length > 0, rConverter = error === '}' && nextChar === '-' && variants.length > 0, brokenExtLink = lbrack && nextType === 'free-ext-link' && !data.slice(index + 1).trim()
|
|
148
139
|
|| rbrack && previousType === 'free-ext-link' && index === 0;
|
|
149
140
|
if (magicLink) {
|
|
@@ -274,7 +265,7 @@ class AstText extends node_1.AstNode {
|
|
|
274
265
|
if (char === '<') {
|
|
275
266
|
e.suggestions = [(0, lint_1.fixByEscape)(startIndex, '<')];
|
|
276
267
|
}
|
|
277
|
-
else if (char === 'h' && type !== 'link-text' &&
|
|
268
|
+
else if (char === 'h' && type !== 'link-text' && /[\p{L}\p{N}_]/u.test(previousChar ?? '')) {
|
|
278
269
|
e.suggestions = [(0, lint_1.fixBySpace)(startIndex)];
|
|
279
270
|
}
|
|
280
271
|
else if (lbrackInExtLinkText) {
|
package/dist/lib/title.js
CHANGED
|
@@ -11,6 +11,7 @@ const resolve = (title) => {
|
|
|
11
11
|
const [, { length }, sub] = /^((?:\.\.\/)*)(.*)/su.exec(title);
|
|
12
12
|
return [length / 3, sub];
|
|
13
13
|
};
|
|
14
|
+
const capitalLinkNs = new Set([-1, 2, 3, 8, 9]);
|
|
14
15
|
/**
|
|
15
16
|
* title object of a MediaWiki page
|
|
16
17
|
*
|
|
@@ -22,6 +23,7 @@ class Title {
|
|
|
22
23
|
#namespaces;
|
|
23
24
|
#path;
|
|
24
25
|
#ns;
|
|
26
|
+
#capitalLinks;
|
|
25
27
|
#fragment;
|
|
26
28
|
/** @private */
|
|
27
29
|
page;
|
|
@@ -42,7 +44,9 @@ class Title {
|
|
|
42
44
|
}
|
|
43
45
|
set main(title) {
|
|
44
46
|
title = title.replace(/_/gu, ' ').trim();
|
|
45
|
-
this.#main =
|
|
47
|
+
this.#main = this.#capitalLinks || capitalLinkNs.has(this.#ns)
|
|
48
|
+
? title && title[0].toUpperCase() + title.slice(1)
|
|
49
|
+
: title;
|
|
46
50
|
}
|
|
47
51
|
/** namespace prefix / 命名空间前缀 */
|
|
48
52
|
get prefix() {
|
|
@@ -129,6 +133,7 @@ class Title {
|
|
|
129
133
|
&& (0, string_1.decodeHtml)(title) === title
|
|
130
134
|
&& (level === 0 || page === undefined || page.split('/', level + 1).length > level)
|
|
131
135
|
&& !/^:|\0\d+[eh!+-]\x7F|[<>[\]{}|\n]|%[\da-f]{2}|(?:^|\/)\.{1,2}(?:$|\/)/iu.test(sub);
|
|
136
|
+
this.#capitalLinks = config.capitalLinks !== false;
|
|
132
137
|
this.main = title;
|
|
133
138
|
this.#namespaces = config.namespaces;
|
|
134
139
|
this.#path = config.articlePath || '/wiki/$1';
|
|
@@ -3,23 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.parseCommentAndExt = void 0;
|
|
4
4
|
const common_1 = require("@bhsd/common");
|
|
5
5
|
const string_1 = require("../util/string");
|
|
6
|
+
const replace_1 = require("../util/replace");
|
|
6
7
|
const onlyinclude_1 = require("../src/onlyinclude");
|
|
7
8
|
const noinclude_1 = require("../src/nowiki/noinclude");
|
|
8
9
|
const include_1 = require("../src/tagPair/include");
|
|
9
10
|
const ext_1 = require("../src/tagPair/ext");
|
|
10
11
|
const comment_1 = require("../src/nowiki/comment");
|
|
11
|
-
const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>',
|
|
12
|
+
const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', getExtRegex = [false, true].map(includeOnly => {
|
|
12
13
|
const noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly';
|
|
13
|
-
return (0, common_1.getRegex)(exts => new RegExp(String.raw `<!--.*?(?:-->|$)|<${noincludeRegex}(
|
|
14
|
+
return (0, common_1.getRegex)(exts => new RegExp(String.raw `<!--.*?(?:-->|$)|<${noincludeRegex}(?=\s|/?>)|</${noincludeRegex}\s*>|<(${exts})(?=\s|/?>)|<(${includeRegex})(?=\s|/?>)`, 'gisu'));
|
|
14
15
|
});
|
|
15
|
-
/**
|
|
16
|
-
* 更新`<onlyinclude>`和`</onlyinclude>`的位置
|
|
17
|
-
* @param wikitext
|
|
18
|
-
*/
|
|
19
|
-
const update = (wikitext) => {
|
|
20
|
-
const i = wikitext.indexOf(onlyincludeLeft);
|
|
21
|
-
return { i, j: wikitext.indexOf(onlyincludeRight, i + length) };
|
|
22
|
-
};
|
|
23
16
|
/**
|
|
24
17
|
* 解析HTML注释和扩展标签
|
|
25
18
|
* @param wikitext
|
|
@@ -29,32 +22,20 @@ const update = (wikitext) => {
|
|
|
29
22
|
*/
|
|
30
23
|
const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
|
|
31
24
|
if (includeOnly) {
|
|
32
|
-
|
|
33
|
-
if (i !== -1 &&
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* 忽略未被`<onlyinclude>`和`</onlyinclude>`包裹的内容
|
|
37
|
-
* @param text 未被包裹的内容
|
|
38
|
-
*/
|
|
39
|
-
const noinclude = (text) => {
|
|
40
|
-
// @ts-expect-error abstract class
|
|
41
|
-
new noinclude_1.NoincludeToken(text, config, accum);
|
|
42
|
-
str += `\0${accum.length - 1}n\x7F`;
|
|
43
|
-
};
|
|
44
|
-
while (i !== -1 && j !== -1) {
|
|
25
|
+
const i = wikitext.indexOf(onlyincludeLeft);
|
|
26
|
+
if (i !== -1 && wikitext.includes(onlyincludeRight, i)) { // `<onlyinclude>`拥有最高优先级
|
|
27
|
+
return (0, replace_1.safeStringReplace)(wikitext, onlyincludeLeft, onlyincludeRight, false, inner => {
|
|
45
28
|
const token = `\0${accum.length}g\x7F`;
|
|
46
|
-
new onlyinclude_1.OnlyincludeToken(
|
|
47
|
-
|
|
48
|
-
|
|
29
|
+
new onlyinclude_1.OnlyincludeToken(inner, config, accum);
|
|
30
|
+
return token;
|
|
31
|
+
}, s => {
|
|
32
|
+
if (!s) {
|
|
33
|
+
return '';
|
|
49
34
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
if (wikitext) {
|
|
55
|
-
noinclude(wikitext);
|
|
56
|
-
}
|
|
57
|
-
return str;
|
|
35
|
+
// @ts-expect-error abstract class
|
|
36
|
+
new noinclude_1.NoincludeToken(s, config, accum);
|
|
37
|
+
return `\0${accum.length - 1}n\x7F`;
|
|
38
|
+
});
|
|
58
39
|
}
|
|
59
40
|
}
|
|
60
41
|
const { ext } = config;
|
|
@@ -64,45 +45,103 @@ const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
|
|
|
64
45
|
const stack = [];
|
|
65
46
|
newExt = ext.filter(e => e !== 'translate' && e !== 'tvar');
|
|
66
47
|
newConfig = { ...config, ext: newExt };
|
|
67
|
-
|
|
68
|
-
|
|
48
|
+
// 隐藏`<nowiki>`标签
|
|
49
|
+
const text = (0, replace_1.safeStringReplace)(wikitext, '<nowiki>', '</nowiki>', true, (inner, open, close) => {
|
|
50
|
+
stack.push(open + inner + close);
|
|
69
51
|
return `\0${stack.length - 1}\x7F`;
|
|
70
|
-
})
|
|
52
|
+
});
|
|
53
|
+
// 解析`<translate>`标签
|
|
54
|
+
wikitext = (0, replace_1.safePatternReplace)(text, /<translate( nowrap)?>/gu, '</translate>', (inner, [, p1]) => {
|
|
71
55
|
const l = accum.length;
|
|
72
56
|
// @ts-expect-error abstract class
|
|
73
|
-
new TranslateToken(p1,
|
|
57
|
+
new TranslateToken(p1, inner && (0, string_1.restore)(inner, stack), newConfig, accum);
|
|
74
58
|
return `\0${l}g\x7F`;
|
|
75
59
|
});
|
|
76
60
|
wikitext = (0, string_1.restore)(wikitext, stack);
|
|
77
61
|
}
|
|
78
|
-
const re = getExtRegex[includeOnly ? 1 : 0](newExt.join('|'));
|
|
62
|
+
const re = getExtRegex[includeOnly ? 1 : 0](newExt.join('|')), noMoreTags = new Set();
|
|
79
63
|
re.lastIndex = 0;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
64
|
+
let lastIndex = 0, text = '', noMoreGt = false;
|
|
65
|
+
for (let mt = re.exec(wikitext); mt; mt = re.exec(wikitext)) {
|
|
66
|
+
const [substr, name, include] = mt, tag = name || include, { index } = mt, l = accum.length;
|
|
67
|
+
text += wikitext.slice(lastIndex, index);
|
|
68
|
+
({ lastIndex } = re);
|
|
69
|
+
let ch = 'n', matched = true;
|
|
70
|
+
if (substr.startsWith('<!--')) {
|
|
71
|
+
ch = 'c';
|
|
72
|
+
const closed = substr.endsWith('-->');
|
|
73
|
+
// @ts-expect-error abstract class
|
|
74
|
+
new comment_1.CommentToken((0, string_1.restore)(substr, accum, 1)
|
|
75
|
+
.slice(4, closed ? -3 : undefined), closed, config, accum);
|
|
76
|
+
}
|
|
77
|
+
else if (noMoreGt) {
|
|
78
|
+
matched = false;
|
|
79
|
+
}
|
|
80
|
+
else if (tag) {
|
|
81
|
+
const i = wikitext.indexOf('>', lastIndex);
|
|
82
|
+
let attr, inner, closing;
|
|
83
|
+
if (i === -1) {
|
|
84
|
+
matched = false;
|
|
85
|
+
noMoreGt = true;
|
|
88
86
|
}
|
|
89
|
-
else if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
new comment_1.CommentToken((0, string_1.restore)(substr, accum, 1)
|
|
94
|
-
.slice(4, closed ? -3 : undefined), closed, config, accum);
|
|
87
|
+
else if (wikitext[i - 1] === '/') {
|
|
88
|
+
attr = wikitext.slice(lastIndex, i - 1);
|
|
89
|
+
lastIndex = i + 1;
|
|
90
|
+
re.lastIndex = lastIndex;
|
|
95
91
|
}
|
|
96
|
-
else if (
|
|
97
|
-
|
|
98
|
-
new include_1.IncludeToken(include, includeAttr && (0, string_1.restore)(includeAttr, accum, 1), includeInner && (0, string_1.restore)(includeInner, accum, 1), includeClosing, config, accum);
|
|
92
|
+
else if (noMoreTags.has(tag.toLowerCase())) {
|
|
93
|
+
matched = false;
|
|
99
94
|
}
|
|
100
95
|
else {
|
|
96
|
+
attr = wikitext.slice(lastIndex, i);
|
|
97
|
+
const after = wikitext.slice(i + 1), m = new RegExp(String.raw `</(${tag}\s*)>`, 'iu').exec(after);
|
|
98
|
+
if (m) {
|
|
99
|
+
inner = after.slice(0, m.index);
|
|
100
|
+
[, closing] = m;
|
|
101
|
+
lastIndex = i + 1 + m.index + m[0].length;
|
|
102
|
+
re.lastIndex = lastIndex;
|
|
103
|
+
}
|
|
104
|
+
else if (include) {
|
|
105
|
+
inner = after;
|
|
106
|
+
lastIndex = wikitext.length;
|
|
107
|
+
re.lastIndex = lastIndex;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
matched = false;
|
|
111
|
+
noMoreTags.add(tag.toLowerCase());
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (matched) {
|
|
115
|
+
if (name) {
|
|
116
|
+
ch = 'e';
|
|
117
|
+
// @ts-expect-error abstract class
|
|
118
|
+
new ext_1.ExtToken(name, attr, inner, closing, newConfig, include, accum);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// @ts-expect-error abstract class
|
|
122
|
+
new include_1.IncludeToken(include, attr && (0, string_1.restore)(attr, accum, 1), inner && (0, string_1.restore)(inner, accum, 1), closing, config, accum);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else if (substr.startsWith('</')) {
|
|
127
|
+
// @ts-expect-error abstract class
|
|
128
|
+
new noinclude_1.NoincludeToken(wikitext.slice(index, lastIndex), config, accum, true);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const i = wikitext.indexOf('>', lastIndex);
|
|
132
|
+
if (i === -1) {
|
|
133
|
+
matched = false;
|
|
134
|
+
noMoreGt = true;
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
lastIndex = i + 1;
|
|
138
|
+
re.lastIndex = lastIndex;
|
|
101
139
|
// @ts-expect-error abstract class
|
|
102
|
-
new noinclude_1.NoincludeToken(
|
|
140
|
+
new noinclude_1.NoincludeToken(wikitext.slice(index, lastIndex), config, accum, true);
|
|
103
141
|
}
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
142
|
+
}
|
|
143
|
+
text += matched ? `\0${l}${ch}\x7F` : substr;
|
|
144
|
+
}
|
|
145
|
+
return text + wikitext.slice(lastIndex);
|
|
107
146
|
};
|
|
108
147
|
exports.parseCommentAndExt = parseCommentAndExt;
|
package/dist/parser/converter.js
CHANGED
|
@@ -11,8 +11,8 @@ const converter_1 = require("../src/converter");
|
|
|
11
11
|
const parseConverter = (text, config, accum) => {
|
|
12
12
|
config.regexConverter ??= new RegExp(String.raw `;(?=(?:[^;]*?=>)?\s*(?:${config.variants.join('|')})\s*:|(?:\s|\0\d+[cn]\x7F)*$)`, 'u');
|
|
13
13
|
const regex1 = /-\{/gu, regex2 = /-\{|\}-/gu, stack = [];
|
|
14
|
-
let regex = regex1
|
|
15
|
-
|
|
14
|
+
let regex = regex1;
|
|
15
|
+
for (let mt = regex.exec(text); mt; mt = regex.exec(text)) {
|
|
16
16
|
const { 0: syntax, index } = mt;
|
|
17
17
|
if (syntax === '}-') {
|
|
18
18
|
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'), rules = temp.split(config.regexConverter)
|
|
@@ -30,7 +30,6 @@ const parseConverter = (text, config, accum) => {
|
|
|
30
30
|
regex = regex2;
|
|
31
31
|
regex.lastIndex = index + 2;
|
|
32
32
|
}
|
|
33
|
-
mt = regex.exec(text);
|
|
34
33
|
}
|
|
35
34
|
return text;
|
|
36
35
|
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseExternalLinks = void 0;
|
|
4
4
|
const string_1 = require("../util/string");
|
|
5
|
+
const replace_1 = require("../util/replace");
|
|
5
6
|
const extLink_1 = require("../src/extLink");
|
|
6
7
|
const magicLink_1 = require("../src/magicLink");
|
|
7
8
|
/**
|
|
@@ -12,13 +13,17 @@ const magicLink_1 = require("../src/magicLink");
|
|
|
12
13
|
* @param inFile 是否在图链中
|
|
13
14
|
*/
|
|
14
15
|
const parseExternalLinks = (wikitext, config, accum, inFile) => {
|
|
15
|
-
config.regexExternalLinks ??= new RegExp(String.raw `\[((?:\0\d+[cn]\x7F)*(?:\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}]))
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (
|
|
16
|
+
config.regexExternalLinks ??= new RegExp(String.raw `\[((?:\0\d+[cn]\x7F)*(?:\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}]))`, 'giu');
|
|
17
|
+
const re = config.regexExternalLinks;
|
|
18
|
+
return (0, replace_1.safePatternReplace)(wikitext, re, ']', (text, [, url, space]) => {
|
|
19
|
+
if (/[\x01-\x08\x0A-\x1F\uFFFD]/u.test(text)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const { length } = accum, i = url.search(/&[lg]t;/u);
|
|
23
|
+
if (i !== -1) {
|
|
19
24
|
space = '';
|
|
20
|
-
text = url.slice(
|
|
21
|
-
url = url.slice(0,
|
|
25
|
+
text = url.slice(i) + space + text;
|
|
26
|
+
url = url.slice(0, i);
|
|
22
27
|
}
|
|
23
28
|
if (inFile) {
|
|
24
29
|
// @ts-expect-error abstract class
|
|
@@ -11,23 +11,15 @@ const space = String.raw `[${string_1.zs}\t]| |�*160;|�*a0;`, sp = `(?
|
|
|
11
11
|
* @param accum
|
|
12
12
|
*/
|
|
13
13
|
const parseMagicLinks = (wikitext, config, accum) => {
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
config.regexMagicLinks = new RegExp(String.raw `(^|[^\p{L}\p{N}_])(?:(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})|${magicLinkPattern})`, 'giu');
|
|
17
|
-
}
|
|
18
|
-
catch /* c8 ignore start */ {
|
|
19
|
-
config.regexMagicLinks = new RegExp(String.raw `(^|\W)(?:(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})|${magicLinkPattern})`, 'giu');
|
|
20
|
-
}
|
|
21
|
-
/* c8 ignore stop */
|
|
22
|
-
}
|
|
14
|
+
config.regexMagicLinks ??= new RegExp(String.raw `(^|[^\p{L}\p{N}_])(?:(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})|${magicLinkPattern})`, 'giu');
|
|
23
15
|
return wikitext.replace(config.regexMagicLinks, (m, lead, p1) => {
|
|
24
16
|
let url = lead ? m.slice(lead.length) : m;
|
|
25
17
|
if (p1) {
|
|
26
18
|
let trail = '';
|
|
27
|
-
const
|
|
28
|
-
if (
|
|
29
|
-
trail = url.slice(
|
|
30
|
-
url = url.slice(0,
|
|
19
|
+
const index = url.search(/&(?:[lg]t|nbsp|#x0*(?:3[ce]|a0)|#0*(?:6[02]|160));/iu);
|
|
20
|
+
if (index !== -1) {
|
|
21
|
+
trail = url.slice(index);
|
|
22
|
+
url = url.slice(0, index);
|
|
31
23
|
}
|
|
32
24
|
const sep = url.includes('(') ? /[^,;\\.:!?][,;\\.:!?]+$/u : /[^,;\\.:!?)][,;\\.:!?)]+$/u, sepChars = sep.exec(url);
|
|
33
25
|
if (sepChars) {
|
package/dist/parser/table.js
CHANGED
|
@@ -100,7 +100,7 @@ const parseTable = ({ firstChild: { data }, type, name }, config, accum) => {
|
|
|
100
100
|
else {
|
|
101
101
|
top = pop(top, stack);
|
|
102
102
|
const regex = cell === '!' ? /!!|(?:\||\0\d+!\x7F){2}|\0\d+\+\x7F/gu : /(?:\||\0\d+!\x7F){2}|\0\d+\+\x7F/gu;
|
|
103
|
-
let
|
|
103
|
+
let lastIndex = 0, lastSyntax = `\n${spaces}${cell}`, mt;
|
|
104
104
|
/**
|
|
105
105
|
* 创建表格单元格
|
|
106
106
|
* @param tr 当前表格行
|
|
@@ -111,11 +111,10 @@ const parseTable = ({ firstChild: { data }, type, name }, config, accum) => {
|
|
|
111
111
|
tr.insertAt(td);
|
|
112
112
|
return td;
|
|
113
113
|
};
|
|
114
|
-
|
|
114
|
+
for (mt = regex.exec(attr); mt; mt = regex.exec(attr)) {
|
|
115
115
|
newTd(top);
|
|
116
116
|
({ lastIndex } = regex);
|
|
117
117
|
[lastSyntax] = mt;
|
|
118
|
-
mt = regex.exec(attr);
|
|
119
118
|
}
|
|
120
119
|
stack.push(top, newTd(top));
|
|
121
120
|
}
|
package/dist/src/attribute.js
CHANGED
|
@@ -14,7 +14,7 @@ const index_2 = require("./index");
|
|
|
14
14
|
const atom_1 = require("./atom");
|
|
15
15
|
/* NOT FOR BROWSER ONLY */
|
|
16
16
|
const document_1 = require("../lib/document");
|
|
17
|
-
const insecureStyle = /expression|(?:accelerator|-o-link(?:-source)?|-o-replace)\s*:|(?:url|src|image(?:-set)?)\s*\(|attr\s*\([^)]+[\s,]url/u, evil = /(?:^|\s|\*\/)(?:java|vb)script(?:\W|$)/iu, complexTypes = new Set(['ext', 'arg', 'magic-word', 'template']), itemAttrs = new Set(['itemtype', 'itemid', 'itemref']), urlAttrs = new Set([
|
|
17
|
+
const insecureStyle = /expression|(?:accelerator|-o-link(?:-source)?|-o-replace)\s*:|(?:url|src|image(?:-set)?)\s*\(|attr\s*\((?:(?!attr\s*\()[^)])+[\s,]url/u, evil = /(?:^|\s|\*\/)(?:java|vb)script(?:\W|$)/iu, complexTypes = new Set(['ext', 'arg', 'magic-word', 'template']), itemAttrs = new Set(['itemtype', 'itemid', 'itemref']), urlAttrs = new Set([
|
|
18
18
|
'about',
|
|
19
19
|
'property',
|
|
20
20
|
'resource',
|
package/dist/src/attributes.js
CHANGED
|
@@ -22,16 +22,6 @@ exports.toAttributeType = toAttributeType;
|
|
|
22
22
|
* @param type 属性类型
|
|
23
23
|
*/
|
|
24
24
|
const toDirty = (type) => `${(0, exports.toAttributeType)(type)}-dirty`;
|
|
25
|
-
const wordRegex = /* #__PURE__ */ (() => {
|
|
26
|
-
try {
|
|
27
|
-
// eslint-disable-next-line prefer-regex-literals
|
|
28
|
-
return new RegExp(String.raw `[\p{L}\p{N}]`, 'u');
|
|
29
|
-
}
|
|
30
|
-
catch /* c8 ignore start */ {
|
|
31
|
-
return /[^\W_]/u;
|
|
32
|
-
}
|
|
33
|
-
/* c8 ignore stop */
|
|
34
|
-
})();
|
|
35
25
|
const required = new Map([
|
|
36
26
|
['indicator', ['name']],
|
|
37
27
|
['langconvert', ['from', 'to']],
|
|
@@ -63,14 +53,14 @@ class AttributesToken extends index_2.Token {
|
|
|
63
53
|
this.setAttribute('name', name);
|
|
64
54
|
if (attr) {
|
|
65
55
|
const regex = /([^\s/](?:(?!\0\d+~\x7F)[^\s/=])*)(?:((?:\s(?:\s|\0\d+[cn]\x7F)*)?(?:=|\0\d+~\x7F)(?:\s|\0\d+[cn]\x7F)*)(?:(["'])(.*?)(\3|$)|(\S*)))?/gsu;
|
|
66
|
-
let out = '',
|
|
56
|
+
let out = '', lastIndex = 0;
|
|
67
57
|
const insertDirty = /** 插入无效属性 */ () => {
|
|
68
58
|
if (out) {
|
|
69
59
|
super.insertAt(new atom_1.AtomToken(out, toDirty(type), config, accum));
|
|
70
60
|
out = '';
|
|
71
61
|
}
|
|
72
62
|
};
|
|
73
|
-
|
|
63
|
+
for (let mt = regex.exec(attr); mt; mt = regex.exec(attr)) {
|
|
74
64
|
const { index, 0: full, 1: key, 2: equal, 3: quoteStart, 4: quoted, 5: quoteEnd, 6: unquoted } = mt;
|
|
75
65
|
out += attr.slice(lastIndex, index);
|
|
76
66
|
if (/^(?:[\w:]|\0\d+t\x7F)(?:[\w:.-]|\0\d+t\x7F)*$/u
|
|
@@ -85,7 +75,6 @@ class AttributesToken extends index_2.Token {
|
|
|
85
75
|
out += full;
|
|
86
76
|
}
|
|
87
77
|
({ lastIndex } = regex);
|
|
88
|
-
mt = regex.exec(attr);
|
|
89
78
|
}
|
|
90
79
|
out += attr.slice(lastIndex);
|
|
91
80
|
insertDirty();
|
|
@@ -185,7 +174,7 @@ class AttributesToken extends index_2.Token {
|
|
|
185
174
|
}
|
|
186
175
|
}
|
|
187
176
|
else {
|
|
188
|
-
const str = attr.text().trim(), severity = s[
|
|
177
|
+
const str = attr.text().trim(), severity = s[/[\p{L}\p{N}]/u.test(str) ? 1 : 2];
|
|
189
178
|
if (str && severity) {
|
|
190
179
|
const e = (0, lint_1.generateForChild)(attr, rect, rules[0], 'invalid-attribute', severity);
|
|
191
180
|
if (computeEditInfo) {
|
package/dist/src/commented.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CommentedToken = void 0;
|
|
4
|
+
const replace_1 = require("../util/replace");
|
|
4
5
|
const index_1 = require("./index");
|
|
5
6
|
const comment_1 = require("./nowiki/comment");
|
|
6
7
|
/**
|
|
@@ -15,25 +16,19 @@ class CommentedToken extends index_1.Token {
|
|
|
15
16
|
constructor(wikitext, config, accum = []) {
|
|
16
17
|
super(undefined, config, accum, {});
|
|
17
18
|
if (wikitext) {
|
|
18
|
-
|
|
19
|
-
while (j !== false && j !== -1) {
|
|
20
|
-
if (i > lastIndex) {
|
|
21
|
-
this.insertAt(wikitext.slice(lastIndex, i));
|
|
22
|
-
}
|
|
19
|
+
(0, replace_1.safeStringReplace)(wikitext, '<!--', '-->', false, inner => {
|
|
23
20
|
// @ts-expect-error abstract class
|
|
24
|
-
this.insertAt(new comment_1.CommentToken(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this.insertAt(wikitext.slice(lastIndex));
|
|
31
|
-
}
|
|
21
|
+
this.insertAt(new comment_1.CommentToken(inner, true, config, accum));
|
|
22
|
+
return '';
|
|
23
|
+
}, s => {
|
|
24
|
+
this.insertAt(s);
|
|
25
|
+
return '';
|
|
26
|
+
});
|
|
32
27
|
}
|
|
33
28
|
}
|
|
34
29
|
/** @private */
|
|
35
30
|
lint(start = this.getAbsoluteIndex()) {
|
|
36
|
-
LINT: return super.lint(start, new RegExp(String.raw `<\s*(
|
|
31
|
+
LINT: return super.lint(start, new RegExp(String.raw `<\s*(?:/\s*)?(${this.name})\b`, 'giu'));
|
|
37
32
|
}
|
|
38
33
|
}
|
|
39
34
|
exports.CommentedToken = CommentedToken;
|
package/dist/src/index.js
CHANGED
|
@@ -1,48 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// PHP解析器的步骤:
|
|
3
|
-
// -2. 替换签名和`{{subst:}}`,参见Parser::preSaveTransform;这在revision中不可能保留,可以跳过
|
|
4
|
-
// -1. 移除特定字符`\0`和`\x7F`,参见Parser::parse
|
|
5
|
-
// 0. 重定向,参见WikitextContentHandler::extractRedirectTargetAndText
|
|
6
|
-
// 1. 注释/扩展标签('<'相关),参见Preprocessor_Hash::buildDomTreeArrayFromText和Sanitizer::decodeTagAttributes
|
|
7
|
-
// 2. 模板/模板变量/标题,注意rightmost法则,以及`-{`和`[[`可以破坏`{{`或`{{{`语法,
|
|
8
|
-
// 参见Preprocessor_Hash::buildDomTreeArrayFromText
|
|
9
|
-
// 3. HTML标签(允许不匹配),参见Sanitizer::internalRemoveHtmlTags
|
|
10
|
-
// 4. 表格,参见Parser::handleTables
|
|
11
|
-
// 5. 水平线、状态开关和余下的标题,参见Parser::internalParse
|
|
12
|
-
// 6. 内链,含文件和分类,参见Parser::handleInternalLinks2
|
|
13
|
-
// 7. `'`,参见Parser::doQuotes
|
|
14
|
-
// 8. 外链,参见Parser::handleExternalLinks
|
|
15
|
-
// 9. ISBN、RFC和自由外链,参见Parser::handleMagicLinks
|
|
16
|
-
// 10. 段落和列表,参见BlockLevelPass::execute
|
|
17
|
-
// 11. 转换,参见LanguageConverter::recursiveConvertTopLevel
|
|
18
|
-
// \0\d+.\x7F标记Token:
|
|
19
|
-
// !: `{{!}}`专用
|
|
20
|
-
// {: `{{(!}}`专用
|
|
21
|
-
// }: `{{!)}}`专用
|
|
22
|
-
// -: `{{!-}}`专用
|
|
23
|
-
// +: `{{!!}}`专用
|
|
24
|
-
// ~: `{{=}}`专用
|
|
25
|
-
// a: AttributeToken
|
|
26
|
-
// b: TableToken
|
|
27
|
-
// c: CommentToke
|
|
28
|
-
// d: ListToken
|
|
29
|
-
// e: ExtToken
|
|
30
|
-
// f: ImageParameterToken内的MagicLinkToken
|
|
31
|
-
// g: TranslateToken或OnlyincludeToken
|
|
32
|
-
// h: HeadingToken
|
|
33
|
-
// i: RFC/PMID/ISBN
|
|
34
|
-
// l: LinkToken
|
|
35
|
-
// m: `{{server}}`、`{{fullurl:}}`、`{{canonicalurl:}}`或`{{filepath:}}`
|
|
36
|
-
// n: NoIncludeToken、IncludeToken、TvarToken、DoubleUnderscoreToken或`{{#vardefine:}}`
|
|
37
|
-
// o: RedirectToken
|
|
38
|
-
// q: QuoteToken
|
|
39
|
-
// r: HrToken
|
|
40
|
-
// s: `{{{|subst:}}}`
|
|
41
|
-
// t: ArgToken或TranscludeToken
|
|
42
|
-
// u: `__toc__`
|
|
43
|
-
// v: ConverterToken
|
|
44
|
-
// w: ExtLinkToken或free-ext-link
|
|
45
|
-
// x: HtmlToken
|
|
46
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
47
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
48
4
|
};
|
|
@@ -429,8 +385,7 @@ class Token extends element_1.AstElement {
|
|
|
429
385
|
}
|
|
430
386
|
if (!ignoreDisables) {
|
|
431
387
|
const regex = new RegExp(String.raw `<!--\s*${configurationComment}-(disable(?:(?:-next)?-line)?|enable)(\s[\sa-z,-]*)?-->`, 'gu'), ignores = [];
|
|
432
|
-
let mt = regex.exec(wikitext);
|
|
433
|
-
while (mt) {
|
|
388
|
+
for (let mt = regex.exec(wikitext); mt; mt = regex.exec(wikitext)) {
|
|
434
389
|
const { 1: type, index } = mt, detail = mt[2]?.trim();
|
|
435
390
|
let line;
|
|
436
391
|
if (type === 'disable-line' || type === 'disable-next-line') {
|
|
@@ -442,7 +397,6 @@ class Token extends element_1.AstElement {
|
|
|
442
397
|
to: type === 'enable' ? regex.lastIndex : undefined,
|
|
443
398
|
rules: detail ? new Set(detail.split(',').map(rule => rule.trim())) : undefined,
|
|
444
399
|
});
|
|
445
|
-
mt = regex.exec(wikitext);
|
|
446
400
|
}
|
|
447
401
|
errors = errors.filter(({ rule, startLine, startIndex }) => {
|
|
448
402
|
const nearest = { pos: 0 };
|
package/dist/src/link/file.js
CHANGED
|
@@ -25,8 +25,8 @@ const explode = (str) => {
|
|
|
25
25
|
return [];
|
|
26
26
|
}
|
|
27
27
|
const regex = /-\{|\}-|\|/gu, exploded = [];
|
|
28
|
-
let
|
|
29
|
-
|
|
28
|
+
let depth = 0, lastIndex = 0;
|
|
29
|
+
for (let mt = regex.exec(str); mt; mt = regex.exec(str)) {
|
|
30
30
|
const { 0: match, index } = mt;
|
|
31
31
|
if (match !== '|') {
|
|
32
32
|
depth += match === '-{' ? 1 : -1;
|
|
@@ -35,7 +35,6 @@ const explode = (str) => {
|
|
|
35
35
|
exploded.push(str.slice(lastIndex, index));
|
|
36
36
|
({ lastIndex } = regex);
|
|
37
37
|
}
|
|
38
|
-
mt = regex.exec(str);
|
|
39
38
|
}
|
|
40
39
|
exploded.push(str.slice(lastIndex));
|
|
41
40
|
return exploded;
|
|
@@ -92,7 +92,7 @@ let GalleryImageToken = (() => {
|
|
|
92
92
|
/** @private */
|
|
93
93
|
getTitle(temporary) {
|
|
94
94
|
const imagemap = this.type === 'imagemap-image';
|
|
95
|
-
return this.normalizeTitle(this.firstChild
|
|
95
|
+
return this.normalizeTitle(this.firstChild[imagemap ? 'toString' : 'text'](), imagemap ? 0 : 6, { halfParsed: true, temporary, decode: !imagemap, page: '' });
|
|
96
96
|
}
|
|
97
97
|
/** 判定无效的图片 */
|
|
98
98
|
#lint() {
|
|
@@ -5,10 +5,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.GalleryToken = void 0;
|
|
7
7
|
const lint_1 = require("../../util/lint");
|
|
8
|
+
const string_1 = require("../../util/string");
|
|
9
|
+
const replace_1 = require("../../util/replace");
|
|
8
10
|
const index_1 = __importDefault(require("../../index"));
|
|
9
11
|
const index_2 = require("./index");
|
|
10
12
|
const galleryImage_1 = require("../link/galleryImage");
|
|
11
13
|
const commentLine_1 = require("../nowiki/commentLine");
|
|
14
|
+
const comment_1 = require("../nowiki/comment");
|
|
12
15
|
/**
|
|
13
16
|
* `<gallery>`
|
|
14
17
|
* @classdesc `{childNodes: (GalleryImageToken|CommentLineToken|AstText)[]}`
|
|
@@ -18,7 +21,12 @@ class GalleryToken extends index_2.MultiLineToken {
|
|
|
18
21
|
constructor(inner, config, accum = []) {
|
|
19
22
|
super(undefined, config, accum, {});
|
|
20
23
|
if (inner) {
|
|
21
|
-
for (
|
|
24
|
+
for (let line of inner.split('\n')) {
|
|
25
|
+
line = (0, replace_1.safeStringReplace)(line, '<!--', '-->', false, p => {
|
|
26
|
+
// @ts-expect-error abstract class
|
|
27
|
+
new comment_1.CommentToken(p, true, config, accum);
|
|
28
|
+
return `\0${accum.length}c\x7F`;
|
|
29
|
+
});
|
|
22
30
|
const matches = /^([^|]+)(?:\|(.*))?/u.exec(line);
|
|
23
31
|
if (!matches) {
|
|
24
32
|
super.insertAt((line.trim()
|
|
@@ -28,7 +36,7 @@ class GalleryToken extends index_2.MultiLineToken {
|
|
|
28
36
|
continue;
|
|
29
37
|
}
|
|
30
38
|
const [, file, alt] = matches;
|
|
31
|
-
if (this.#checkFile(file)) { // eslint-disable-line unicorn/prefer-ternary
|
|
39
|
+
if (this.#checkFile(file, true)) { // eslint-disable-line unicorn/prefer-ternary
|
|
32
40
|
// @ts-expect-error abstract class
|
|
33
41
|
super.insertAt(new galleryImage_1.GalleryImageToken('gallery', file, alt, config, accum));
|
|
34
42
|
}
|
|
@@ -43,9 +51,10 @@ class GalleryToken extends index_2.MultiLineToken {
|
|
|
43
51
|
/**
|
|
44
52
|
* 检查文件名是否有效
|
|
45
53
|
* @param file 文件名
|
|
54
|
+
* @param parsing 是否在解析过程中
|
|
46
55
|
*/
|
|
47
|
-
#checkFile(file) {
|
|
48
|
-
return this.normalizeTitle(file, 6, { halfParsed: true, temporary: true, decode: true, page: '' }).valid;
|
|
56
|
+
#checkFile(file, parsing) {
|
|
57
|
+
return this.normalizeTitle(parsing ? (0, string_1.removeComment)(file) : file, 6, { halfParsed: true, temporary: true, decode: true, page: '' }).valid;
|
|
49
58
|
}
|
|
50
59
|
/** @private */
|
|
51
60
|
lint(start = this.getAbsoluteIndex(), re) {
|
package/dist/src/nested.js
CHANGED
|
@@ -40,13 +40,51 @@ class NestedToken extends index_2.Token {
|
|
|
40
40
|
wikitext &&= (0, braces_1.parseBraces)(wikitext, config, accum);
|
|
41
41
|
accum.splice(length, 1);
|
|
42
42
|
}
|
|
43
|
-
else {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
else if (wikitext) {
|
|
44
|
+
const noMoreTags = new Set();
|
|
45
|
+
regex.lastIndex = 0;
|
|
46
|
+
let lastIndex = 0, text = '';
|
|
47
|
+
for (let mt = regex.exec(wikitext); mt; mt = regex.exec(wikitext)) {
|
|
48
|
+
const i = wikitext.indexOf('>', regex.lastIndex);
|
|
49
|
+
if (i === -1) {
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
const [substr, name] = mt, { index } = mt;
|
|
53
|
+
text += wikitext.slice(lastIndex, index);
|
|
54
|
+
({ lastIndex } = regex);
|
|
55
|
+
let matched = true, attr, inner, closing;
|
|
56
|
+
if (wikitext[i - 1] === '/') {
|
|
57
|
+
attr = wikitext.slice(lastIndex, i - 1);
|
|
58
|
+
lastIndex = i + 1;
|
|
59
|
+
regex.lastIndex = lastIndex;
|
|
60
|
+
}
|
|
61
|
+
else if (noMoreTags.has(name.toLowerCase())) {
|
|
62
|
+
matched = false;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
const after = wikitext.slice(i + 1), m = new RegExp(String.raw `</(${name}\s*)>`, 'iu').exec(after);
|
|
66
|
+
if (m) {
|
|
67
|
+
inner = after.slice(0, m.index);
|
|
68
|
+
[, closing] = m;
|
|
69
|
+
lastIndex = i + 1 + m.index + m[0].length;
|
|
70
|
+
regex.lastIndex = lastIndex;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
matched = false;
|
|
74
|
+
noMoreTags.add(name.toLowerCase());
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (matched) {
|
|
78
|
+
const str = `\0${accum.length + 1}e\x7F`;
|
|
79
|
+
// @ts-expect-error abstract class
|
|
80
|
+
new ext_1.ExtToken(name, attr, inner, closing, config, false, accum);
|
|
81
|
+
text += str;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
text += substr;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
wikitext = text + wikitext.slice(lastIndex);
|
|
50
88
|
}
|
|
51
89
|
wikitext &&= wikitext.replace(/(^|\0\d+.\x7F)([^\0]+)(?=$|\0\d+.\x7F)/gu, (_, lead, substr) => {
|
|
52
90
|
// @ts-expect-error abstract class
|
package/dist/src/parameter.js
CHANGED
|
@@ -8,7 +8,7 @@ const string_1 = require("../util/string");
|
|
|
8
8
|
const lint_1 = require("../util/lint");
|
|
9
9
|
const index_1 = __importDefault(require("../index"));
|
|
10
10
|
const index_2 = require("./index");
|
|
11
|
-
const linkRegex = /* #__PURE__ */ (() => new RegExp(
|
|
11
|
+
const linkRegex = /* #__PURE__ */ (() => new RegExp(`^https?://${string_1.extUrlCharFirst}${string_1.extUrlChar}$`, 'u'))();
|
|
12
12
|
/**
|
|
13
13
|
* template or magic word parameter
|
|
14
14
|
*
|
|
@@ -74,9 +74,9 @@ class ParameterToken extends index_2.Token {
|
|
|
74
74
|
LINT: {
|
|
75
75
|
const errors = super.lint(start, re), rule = 'unescaped', { lintConfig } = index_1.default, s = lintConfig.getSeverity(rule);
|
|
76
76
|
if (s) {
|
|
77
|
-
const { firstChild } = this,
|
|
77
|
+
const { firstChild } = this, key = firstChild.text().toLowerCase(), i = Math.max(key.lastIndexOf('http://'), key.lastIndexOf('https://')), link = i !== -1 && key.slice(i);
|
|
78
78
|
try {
|
|
79
|
-
if (link && new URL(link).search) {
|
|
79
|
+
if (link && linkRegex.test(link) && new URL(link).search) {
|
|
80
80
|
const e = (0, lint_1.generateForChild)(firstChild, { start }, rule, 'unescaped-query', s);
|
|
81
81
|
e.startIndex = e.endIndex;
|
|
82
82
|
e.startLine = e.endLine;
|
package/dist/src/pre.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PreToken = void 0;
|
|
4
4
|
const constants_1 = require("../util/constants");
|
|
5
|
+
const replace_1 = require("../util/replace");
|
|
5
6
|
const index_1 = require("./index");
|
|
6
7
|
const noinclude_1 = require("./nowiki/noinclude");
|
|
7
8
|
/**
|
|
@@ -14,29 +15,13 @@ class PreToken extends index_1.Token {
|
|
|
14
15
|
}
|
|
15
16
|
/** @class */
|
|
16
17
|
constructor(wikitext, config, accum = []) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
while (i && j) {
|
|
25
|
-
// @ts-expect-error abstract class
|
|
26
|
-
new noinclude_1.NoincludeToken(i[0], config, accum, true);
|
|
27
|
-
// @ts-expect-error abstract class
|
|
28
|
-
new noinclude_1.NoincludeToken(j[0], config, accum, true);
|
|
29
|
-
str += `${wikitext.slice(lastIndex, i.index)}\0${accum.length - 1}n\x7F${wikitext.slice(i.index + length, j.index)}\0${accum.length}n\x7F`;
|
|
30
|
-
lastIndex = j.index + length + 1;
|
|
31
|
-
opening.lastIndex = lastIndex;
|
|
32
|
-
i = opening.exec(wikitext);
|
|
33
|
-
if (i) {
|
|
34
|
-
closing.lastIndex = i.index + length;
|
|
35
|
-
}
|
|
36
|
-
j = closing.exec(wikitext);
|
|
37
|
-
}
|
|
38
|
-
wikitext = str + wikitext.slice(lastIndex);
|
|
39
|
-
}
|
|
18
|
+
wikitext &&= (0, replace_1.safeStringReplace)(wikitext, '<nowiki>', '</nowiki>', true, (inner, opening, closing) => {
|
|
19
|
+
// @ts-expect-error abstract class
|
|
20
|
+
new noinclude_1.NoincludeToken(opening, config, accum, true);
|
|
21
|
+
// @ts-expect-error abstract class
|
|
22
|
+
new noinclude_1.NoincludeToken(closing, config, accum, true);
|
|
23
|
+
return `\0${accum.length - 1}n\x7F${inner}\0${accum.length}n\x7F`;
|
|
24
|
+
});
|
|
40
25
|
super(wikitext, config, accum, {});
|
|
41
26
|
this.setAttribute('stage', constants_1.MAX_STAGE - 1);
|
|
42
27
|
}
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.CharinsertLineToken = void 0;
|
|
7
7
|
const lint_1 = require("../../util/lint");
|
|
8
|
+
const replace_1 = require("../../util/replace");
|
|
8
9
|
const rect_1 = require("../../lib/rect");
|
|
9
10
|
const index_1 = __importDefault(require("../../index"));
|
|
10
11
|
const index_2 = require("./index");
|
|
@@ -21,9 +22,9 @@ class CharinsertLineToken extends index_2.SingleLineToken {
|
|
|
21
22
|
}
|
|
22
23
|
/** @class */
|
|
23
24
|
constructor(wikitext, config, accum) {
|
|
24
|
-
super(wikitext
|
|
25
|
+
super(wikitext && (0, replace_1.safeStringReplace)(wikitext, '<nowiki>', '</nowiki>', true, (content, opening, closing) => {
|
|
25
26
|
// @ts-expect-error abstract class
|
|
26
|
-
new ext_1.ExtToken(opening, undefined, content, closing, config, false, accum);
|
|
27
|
+
new ext_1.ExtToken(opening.slice(1, -1), undefined, content, closing.slice(2, -1), config, false, accum);
|
|
27
28
|
return `\0${accum.length - 2}e\x7F`;
|
|
28
29
|
}), config, accum);
|
|
29
30
|
}
|
|
@@ -37,22 +38,21 @@ class CharinsertLineToken extends index_2.SingleLineToken {
|
|
|
37
38
|
const errors = [], str = this.childNodes.map(child => child.type === 'text'
|
|
38
39
|
? child.data
|
|
39
40
|
: `<nowiki>${child.innerText.replace(/[^+]/gu, '_')}</nowiki>`).join(''), re = /(?:^|\s)\S+/gu, rect = new rect_1.BoundingRect(this, start), msg = index_1.default.msg('invalid-content', 'charinsert');
|
|
40
|
-
let mt = re.exec(str);
|
|
41
|
-
while (mt) {
|
|
41
|
+
for (let mt = re.exec(str); mt; mt = re.exec(str)) {
|
|
42
42
|
const [item] = mt;
|
|
43
43
|
let i = item.indexOf('+');
|
|
44
44
|
if (i !== -1 && item.slice(0, i).trim()) {
|
|
45
45
|
i = item.indexOf('+', i + 1);
|
|
46
46
|
}
|
|
47
|
-
if (i
|
|
48
|
-
|
|
49
|
-
e.endIndex = start + endOffset;
|
|
50
|
-
e.endCol = e.startCol + endOffset;
|
|
51
|
-
e.startIndex += startOffset;
|
|
52
|
-
e.startCol += startOffset;
|
|
53
|
-
errors.push(e);
|
|
47
|
+
if (i === -1) {
|
|
48
|
+
continue;
|
|
54
49
|
}
|
|
55
|
-
|
|
50
|
+
const e = (0, lint_1.generateForSelf)(this, rect, rule, msg, s), startOffset = mt.index + i, endOffset = mt.index + item.length;
|
|
51
|
+
e.endIndex = start + endOffset;
|
|
52
|
+
e.endCol = e.startCol + endOffset;
|
|
53
|
+
e.startIndex += startOffset;
|
|
54
|
+
e.startCol += startOffset;
|
|
55
|
+
errors.push(e);
|
|
56
56
|
}
|
|
57
57
|
return errors;
|
|
58
58
|
}
|
package/dist/src/tagPair/ext.js
CHANGED
|
@@ -152,14 +152,14 @@ let ExtToken = (() => {
|
|
|
152
152
|
// NestedToken 依赖 ExtToken
|
|
153
153
|
const { NestedToken } = require('../nested');
|
|
154
154
|
// @ts-expect-error abstract class
|
|
155
|
-
innerToken = new NestedToken(inner, /<(option|choicetemplate)(
|
|
155
|
+
innerToken = new NestedToken(inner, /<(option|choicetemplate)(?=\s|\/?>)/gu, ['option', 'choicetemplate'], newConfig, accum);
|
|
156
156
|
break;
|
|
157
157
|
}
|
|
158
158
|
case 'combobox': {
|
|
159
159
|
// NestedToken 依赖 ExtToken
|
|
160
160
|
const { NestedToken } = require('../nested');
|
|
161
161
|
// @ts-expect-error abstract class
|
|
162
|
-
innerToken = new NestedToken(inner, /<(combooption)(
|
|
162
|
+
innerToken = new NestedToken(inner, /<(combooption)(?=\s|\/?>)/gu, ['combooption'], newConfig, accum);
|
|
163
163
|
break;
|
|
164
164
|
}
|
|
165
165
|
case 'gallery': {
|
|
@@ -22,19 +22,49 @@ class TranslateToken extends index_2.TagPairToken {
|
|
|
22
22
|
*/
|
|
23
23
|
constructor(attr, inner, config, accum = []) {
|
|
24
24
|
const attrToken = new syntax_1.SyntaxToken(attr, 'translate-attr', config, accum);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
25
|
+
if (inner) {
|
|
26
|
+
let result = '', lastIndex = 0, i, j, k, index;
|
|
27
|
+
const update = /** @ignore */ () => {
|
|
28
|
+
i = inner.indexOf('<tvar|', lastIndex);
|
|
29
|
+
index = i + 6;
|
|
30
|
+
j = inner.indexOf('>', index);
|
|
31
|
+
k = inner.indexOf('</>', j + 1);
|
|
32
|
+
return [i, j === index ? -1 : j, k];
|
|
33
|
+
};
|
|
34
|
+
for ([i, j, k] = update(); i !== -1 && j !== -1 && k !== -1; [i, j, k] = update()) {
|
|
35
|
+
result += inner.slice(lastIndex, i);
|
|
36
|
+
lastIndex = k + 3;
|
|
37
|
+
// @ts-expect-error abstract class
|
|
38
|
+
new tvar_1.TvarToken('tvar', inner.slice(index - 1, j), false, config, accum);
|
|
39
|
+
// @ts-expect-error abstract class
|
|
40
|
+
new tvar_1.TvarToken('', '', true, config, accum);
|
|
41
|
+
result += `\0${accum.length - 2}n\x7F${inner.slice(j + 1, k)}\0${accum.length}n\x7F`;
|
|
42
|
+
}
|
|
43
|
+
inner = result + inner.slice(lastIndex);
|
|
44
|
+
result = '';
|
|
45
|
+
lastIndex = 0;
|
|
46
|
+
const start = /<(tvar)(\s+name\s*=)/giu, end = /<\/(tvar)(\s*)>/giu;
|
|
47
|
+
for (let mt = start.exec(inner); mt; mt = start.exec(inner)) {
|
|
48
|
+
i = inner.indexOf('>', start.lastIndex);
|
|
49
|
+
const name = inner.slice(start.lastIndex, i);
|
|
50
|
+
if (i === -1 || !/^(?:\s*(?:(["']).*?\1|[^"'\s>]+))?\s*$/su.test(name)) {
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
end.lastIndex = i + 1;
|
|
54
|
+
const mt2 = end.exec(inner);
|
|
55
|
+
if (!mt2) {
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
// @ts-expect-error abstract class
|
|
59
|
+
new tvar_1.TvarToken(mt[1], mt[2] + name, false, config, accum);
|
|
60
|
+
// @ts-expect-error abstract class
|
|
61
|
+
new tvar_1.TvarToken(mt2[1], mt2[2], true, config, accum);
|
|
62
|
+
result += `${inner.slice(lastIndex, mt.index)}\0${accum.length - 2}n\x7F${inner.slice(i + 1, mt2.index)}\0${accum.length}n\x7F`;
|
|
63
|
+
({ lastIndex } = end);
|
|
64
|
+
start.lastIndex = lastIndex;
|
|
65
|
+
}
|
|
66
|
+
inner = result + inner.slice(lastIndex);
|
|
67
|
+
}
|
|
38
68
|
const innerToken = new index_1.Token(inner, config, accum);
|
|
39
69
|
innerToken.type = 'translate-inner';
|
|
40
70
|
super('translate', attrToken, innerToken, 'translate', config, accum);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.safePatternReplace = exports.safeStringReplace = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* 使用`String#indexOf`避免正则表达式的回溯
|
|
6
|
+
* @param wikitext 源文本
|
|
7
|
+
* @param start 起始字符串
|
|
8
|
+
* @param end 结束字符串
|
|
9
|
+
* @param insensitive 是否忽略大小写
|
|
10
|
+
* @param matched 匹配到的内容处理函数
|
|
11
|
+
* @param unmatched 未匹配到的内容处理函数
|
|
12
|
+
*/
|
|
13
|
+
const safeStringReplace = (wikitext, start, end, insensitive, matched, unmatched = (str) => str) => {
|
|
14
|
+
const text = insensitive ? wikitext.toLowerCase() : wikitext, lStart = start.length, lEnd = end.length;
|
|
15
|
+
let result = '', lastIndex = 0, i, j, index;
|
|
16
|
+
const update = /** @ignore */ () => {
|
|
17
|
+
i = text.indexOf(start, lastIndex);
|
|
18
|
+
index = i + lStart;
|
|
19
|
+
return [i, text.indexOf(end, index), index];
|
|
20
|
+
};
|
|
21
|
+
for ([i, j, index] = update(); i !== -1 && j !== -1; [i, j, index] = update()) {
|
|
22
|
+
result += unmatched(wikitext.slice(lastIndex, i));
|
|
23
|
+
lastIndex = j + lEnd;
|
|
24
|
+
result += matched(wikitext.slice(index, j), insensitive ? wikitext.slice(i, index) : start, insensitive ? wikitext.slice(j, lastIndex) : end);
|
|
25
|
+
}
|
|
26
|
+
return result + unmatched(wikitext.slice(lastIndex));
|
|
27
|
+
};
|
|
28
|
+
exports.safeStringReplace = safeStringReplace;
|
|
29
|
+
/**
|
|
30
|
+
* 使用`RegExp#exec`避免正则表达式的回溯
|
|
31
|
+
* @param wikitext 源文本
|
|
32
|
+
* @param start 起始正则表达式
|
|
33
|
+
* @param end 结束字符串
|
|
34
|
+
* @param matched 匹配到的内容处理函数
|
|
35
|
+
*/
|
|
36
|
+
const safePatternReplace = (wikitext, start, end, matched) => {
|
|
37
|
+
let result = '', lastIndex = 0;
|
|
38
|
+
start.lastIndex = 0;
|
|
39
|
+
for (let mt = start.exec(wikitext); mt; mt = start.exec(wikitext)) {
|
|
40
|
+
const i = wikitext.indexOf(end, start.lastIndex);
|
|
41
|
+
if (i === -1) {
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
const { index } = mt;
|
|
45
|
+
result += wikitext.slice(lastIndex, index);
|
|
46
|
+
lastIndex = i + end.length;
|
|
47
|
+
const r = matched(wikitext.slice(start.lastIndex, i), mt);
|
|
48
|
+
if (r === false) {
|
|
49
|
+
({ lastIndex } = start);
|
|
50
|
+
result += wikitext.slice(index, lastIndex);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
result += r;
|
|
54
|
+
start.lastIndex = lastIndex;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return result + wikitext.slice(lastIndex);
|
|
58
|
+
};
|
|
59
|
+
exports.safePatternReplace = safePatternReplace;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wikilint",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "A Node.js linter for MediaWiki markup",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mediawiki",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
77
|
"mathoid-texvcjs": ">=0.4.0",
|
|
78
|
-
"stylelint": ">=
|
|
78
|
+
"stylelint-config-recommended": ">=14.0.0",
|
|
79
79
|
"vscode-css-languageservice": ">=3.0.0",
|
|
80
80
|
"vscode-html-languageservice": ">=3.1.0",
|
|
81
81
|
"vscode-json-languageservice": ">=3.2.0"
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"mathoid-texvcjs": {
|
|
85
85
|
"optional": true
|
|
86
86
|
},
|
|
87
|
-
"stylelint": {
|
|
87
|
+
"stylelint-config-recommended": {
|
|
88
88
|
"optional": true
|
|
89
89
|
},
|
|
90
90
|
"vscode-css-languageservice": {
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"dependencies": {
|
|
101
101
|
"@bhsd/cm-util": "^2.4.2",
|
|
102
102
|
"@bhsd/common": "^4.1.0",
|
|
103
|
-
"@bhsd/stylelint-util": "^
|
|
103
|
+
"@bhsd/stylelint-util": "^2.0.0",
|
|
104
104
|
"culori": "^4.0.2",
|
|
105
105
|
"entities": "^8.1.0",
|
|
106
106
|
"vscode-languageserver-types": "^3.18.5"
|
|
@@ -117,6 +117,7 @@
|
|
|
117
117
|
"eslint": "^10.14.0",
|
|
118
118
|
"mathoid-texvcjs": "^0.6.0",
|
|
119
119
|
"stylelint": "^17.16.0",
|
|
120
|
+
"stylelint-config-recommended": "^18.0.0",
|
|
120
121
|
"typescript": "^6.0.3",
|
|
121
122
|
"vscode-css-languageservice": "^6.3.10",
|
|
122
123
|
"vscode-html-languageservice": "^5.6.2",
|