wikilint 2.28.1 → 2.29.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.
Files changed (51) hide show
  1. package/data/ext/ThirdPartyNotices.txt +33 -0
  2. package/data/ext/mapframe.json +489 -2
  3. package/dist/base.d.mts +4 -2
  4. package/dist/base.d.ts +4 -2
  5. package/dist/base.js +1 -0
  6. package/dist/base.mjs +2 -1
  7. package/dist/bin/config.js +1 -1
  8. package/dist/index.d.ts +2 -1
  9. package/dist/index.js +23 -2
  10. package/dist/lib/document.d.ts +23 -7
  11. package/dist/lib/document.js +7 -27
  12. package/dist/lib/element.js +1 -1
  13. package/dist/lib/lintConfig.js +2 -0
  14. package/dist/lib/lsp.d.ts +1 -12
  15. package/dist/lib/lsp.js +41 -75
  16. package/dist/lib/node.js +23 -20
  17. package/dist/lib/title.d.ts +3 -1
  18. package/dist/lib/title.js +37 -9
  19. package/dist/mixin/elementLike.js +14 -9
  20. package/dist/parser/commentAndExt.js +30 -26
  21. package/dist/parser/links.js +4 -3
  22. package/dist/parser/redirect.js +1 -1
  23. package/dist/parser/selector.js +6 -8
  24. package/dist/src/arg.js +2 -2
  25. package/dist/src/attribute.js +27 -0
  26. package/dist/src/attributes.js +1 -1
  27. package/dist/src/converter.js +6 -3
  28. package/dist/src/imageParameter.d.ts +3 -1
  29. package/dist/src/imageParameter.js +18 -3
  30. package/dist/src/index.d.ts +8 -0
  31. package/dist/src/index.js +21 -28
  32. package/dist/src/link/file.js +7 -7
  33. package/dist/src/link/galleryImage.js +1 -1
  34. package/dist/src/link/redirectTarget.js +1 -1
  35. package/dist/src/multiLine/gallery.js +2 -2
  36. package/dist/src/multiLine/imagemap.js +3 -4
  37. package/dist/src/multiLine/paramTag.js +2 -2
  38. package/dist/src/nowiki/index.js +59 -2
  39. package/dist/src/table/base.js +1 -2
  40. package/dist/src/table/index.js +1 -2
  41. package/dist/src/transclude.js +3 -3
  42. package/dist/util/constants.js +3 -1
  43. package/dist/util/debug.js +1 -1
  44. package/dist/util/search.js +16 -0
  45. package/dist/util/sharable.js +27 -3
  46. package/dist/util/sharable.mjs +28 -4
  47. package/i18n/en.json +2 -0
  48. package/i18n/zh-hans.json +2 -0
  49. package/i18n/zh-hant.json +2 -0
  50. package/package.json +5 -3
  51. package/data/ext/maplink.json +0 -4
@@ -14,16 +14,17 @@ const elementLike = (constructor) => {
14
14
  this);
15
15
  }
16
16
  getElementBy(condition) {
17
- for (const child of this.childNodes) {
18
- if (child.type === 'text') {
17
+ const stack = [...this.childNodes].reverse();
18
+ while (stack.length > 0) {
19
+ const child = stack.pop(), { type, childNodes } = child;
20
+ if (type === 'text') {
19
21
  continue;
20
22
  }
21
23
  else if (condition(child)) {
22
24
  return child;
23
25
  }
24
- const descendant = child.getElementBy(condition);
25
- if (descendant) {
26
- return descendant;
26
+ for (let i = childNodes.length - 1; i >= 0; i--) {
27
+ stack.push(childNodes[i]);
27
28
  }
28
29
  }
29
30
  return undefined;
@@ -31,15 +32,19 @@ const elementLike = (constructor) => {
31
32
  querySelector(selector) {
32
33
  return this.getElementBy(this.#getCondition(selector));
33
34
  }
34
- getElementsBy(condition, descendants = []) {
35
- for (const child of this.childNodes) {
36
- if (child.type === 'text') {
35
+ getElementsBy(condition) {
36
+ const stack = [...this.childNodes].reverse(), descendants = [];
37
+ while (stack.length > 0) {
38
+ const child = stack.pop(), { type, childNodes } = child;
39
+ if (type === 'text') {
37
40
  continue;
38
41
  }
39
42
  else if (condition(child)) {
40
43
  descendants.push(child);
41
44
  }
42
- child.getElementsBy(condition, descendants);
45
+ for (let i = childNodes.length - 1; i >= 0; i--) {
46
+ stack.push(childNodes[i]);
47
+ }
43
48
  }
44
49
  return descendants;
45
50
  }
@@ -9,9 +9,9 @@ const translate_1 = require("../src/tagPair/translate");
9
9
  const include_1 = require("../src/tagPair/include");
10
10
  const ext_1 = require("../src/tagPair/ext");
11
11
  const comment_1 = require("../src/nowiki/comment");
12
- const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', { length } = onlyincludeLeft, getRegex = [false, true].map(includeOnly => {
12
+ const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', { length } = onlyincludeLeft, getExtRegex = [false, true].map(includeOnly => {
13
13
  const noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly';
14
- return (0, common_1.getObjRegex)(ext => new RegExp(String.raw `<!--[\s\S]*?(?:-->|$)|<${noincludeRegex}(?:\s[^>]*)?/?>|</${noincludeRegex}\s*>|<(${ext.join('|') // eslint-disable-next-line unicorn/prefer-string-raw
14
+ return (0, common_1.getRegex)(exts => new RegExp(String.raw `<!--[\s\S]*?(?:-->|$)|<${noincludeRegex}(?:\s[^>]*)?/?>|</${noincludeRegex}\s*>|<(${exts // eslint-disable-next-line unicorn/prefer-string-raw
15
15
  })(\s[^>]*?)?(?:/>|>([\s\S]*?)</(${'\\1'}\s*)>)|<(${includeRegex})(\s[^>]*?)?(?:/>|>([\s\S]*?)(?:</(${includeRegex}\s*)>|$))`, 'giu'));
16
16
  });
17
17
  /**
@@ -76,29 +76,33 @@ const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
76
76
  });
77
77
  wikitext = (0, string_1.restore)(wikitext, stack);
78
78
  }
79
- return wikitext.replace(getRegex[includeOnly ? 1 : 0](newExt), (substr, name, attr, inner, closing, include, includeAttr, includeInner, includeClosing) => {
80
- const l = accum.length;
81
- let ch = 'n';
82
- if (name) {
83
- ch = 'e';
84
- // @ts-expect-error abstract class
85
- new ext_1.ExtToken(name, attr, inner, closing, newConfig, include, accum);
86
- }
87
- else if (substr.startsWith('<!--')) {
88
- ch = 'c';
89
- const closed = substr.endsWith('-->');
90
- // @ts-expect-error abstract class
91
- new comment_1.CommentToken((0, string_1.restore)(substr, accum, 1).slice(4, closed ? -3 : undefined), closed, config, accum);
92
- }
93
- else if (include) {
94
- // @ts-expect-error abstract class
95
- new include_1.IncludeToken(include, includeAttr && (0, string_1.restore)(includeAttr, accum, 1), includeInner && (0, string_1.restore)(includeInner, accum, 1), includeClosing, config, accum);
96
- }
97
- else {
98
- // @ts-expect-error abstract class
99
- new noinclude_1.NoincludeToken(substr, config, accum, true);
100
- }
101
- return `\0${l}${ch}\x7F`;
102
- });
79
+ const re = getExtRegex[includeOnly ? 1 : 0](newExt.join('|'));
80
+ re.lastIndex = 0;
81
+ return re.test(wikitext)
82
+ ? wikitext.replace(re, (substr, name, attr, inner, closing, include, includeAttr, includeInner, includeClosing) => {
83
+ const l = accum.length;
84
+ let ch = 'n';
85
+ if (name) {
86
+ ch = 'e';
87
+ // @ts-expect-error abstract class
88
+ new ext_1.ExtToken(name, attr, inner, closing, newConfig, include, accum);
89
+ }
90
+ else if (substr.startsWith('<!--')) {
91
+ ch = 'c';
92
+ const closed = substr.endsWith('-->');
93
+ // @ts-expect-error abstract class
94
+ new comment_1.CommentToken((0, string_1.restore)(substr, accum, 1).slice(4, closed ? -3 : undefined), closed, config, accum);
95
+ }
96
+ else if (include) {
97
+ // @ts-expect-error abstract class
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);
99
+ }
100
+ else {
101
+ // @ts-expect-error abstract class
102
+ new noinclude_1.NoincludeToken(substr, config, accum, true);
103
+ }
104
+ return `\0${l}${ch}\x7F`;
105
+ })
106
+ : wikitext;
103
107
  };
104
108
  exports.parseCommentAndExt = parseCommentAndExt;
@@ -17,9 +17,10 @@ const regexImg = /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(\||\0\d+!\x7F)([\s\S]*)$/u;
17
17
  * @param wikitext
18
18
  * @param config
19
19
  * @param accum
20
+ * @param page 页面名称
20
21
  * @param tidy 是否整理链接
21
22
  */
22
- const parseLinks = (wikitext, config, accum, tidy) => {
23
+ const parseLinks = (wikitext, config, accum, page, tidy) => {
23
24
  config.regexLinks ??= new RegExp(String.raw `^\s*(?:${config.protocol}|//)`, 'iu');
24
25
  const regex = config.inExt
25
26
  ? /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(?:(\||\0\d+!\x7F)([\s\S]*?[^\]]))?\]\]([\s\S]*)$/u
@@ -53,7 +54,7 @@ const parseLinks = (wikitext, config, accum, tidy) => {
53
54
  s += `[[${x}`;
54
55
  continue;
55
56
  }
56
- const { ns, valid, } = index_1.default.normalizeTitle(trimmed, 0, false, config, { halfParsed: true, temporary: true, decode: true, selfLink: true });
57
+ const { ns, valid, } = index_1.default.normalizeTitle(trimmed, 0, false, config, { halfParsed: true, temporary: true, decode: true, selfLink: true, page });
57
58
  if (!valid) {
58
59
  s += `[[${x}`;
59
60
  continue;
@@ -80,7 +81,7 @@ const parseLinks = (wikitext, config, accum, tidy) => {
80
81
  break;
81
82
  }
82
83
  }
83
- text = (0, exports.parseLinks)(text, config, accum, tidy);
84
+ text = (0, exports.parseLinks)(text, config, accum, page, tidy);
84
85
  if (!found) {
85
86
  s += `[[${link}${delimiter}${text}`;
86
87
  continue;
@@ -16,7 +16,7 @@ const parseRedirect = (text, config, accum) => {
16
16
  config.regexRedirect ??= new RegExp(String.raw `^(\s*)((?:${config.redirection.join('|')})\s*(?::\s*)?)\[\[([^\n|\]]+)(\|.*?)?\]\](\s*)`, 'iu');
17
17
  const mt = config.regexRedirect.exec(text);
18
18
  if (mt
19
- && index_1.default.normalizeTitle(mt[3], 0, false, config, { halfParsed: true, temporary: true, decode: true }).valid) {
19
+ && index_1.default.normalizeTitle(mt[3], 0, false, config, { halfParsed: true, temporary: true, decode: true, page: '' }).valid) {
20
20
  text = `\0${accum.length}o\x7F${text.slice(mt[0].length)}`;
21
21
  // @ts-expect-error abstract class
22
22
  new redirect_1.RedirectToken(...mt.slice(1), config, accum);
@@ -4,15 +4,13 @@ exports.getCondition = void 0;
4
4
  /**
5
5
  * type和name选择器
6
6
  * @param selector
7
- * @param type
8
- * @param name
9
7
  */
10
- const basic = (selector, type, name) => {
8
+ const basic = (selector) => {
11
9
  if (selector.includes('#')) {
12
- const i = selector.indexOf('#');
13
- return (i === 0 || selector.slice(0, i) === type) && selector.slice(i + 1) === name;
10
+ const i = selector.indexOf('#'), targetType = selector.slice(0, i), targetName = selector.slice(i + 1);
11
+ return (type, name) => (i === 0 || type === targetType) && name === targetName;
14
12
  }
15
- return !selector || selector === type;
13
+ return selector ? (type) => type === selector : () => true;
16
14
  };
17
15
  /**
18
16
  * 将选择器转化为类型谓词
@@ -21,7 +19,7 @@ const basic = (selector, type, name) => {
21
19
  * @param has `:has()`伪选择器
22
20
  */
23
21
  const getCondition = (selector, scope, has) => {
24
- const parts = selector.split(',');
25
- return (({ type, name }) => parts.some(str => basic(str.trim(), type, name)));
22
+ const parts = selector.split(',').map(str => basic(str.trim()));
23
+ return (({ type, name }) => parts.some(condition => condition(type, name)));
26
24
  };
27
25
  exports.getCondition = getCondition;
package/dist/src/arg.js CHANGED
@@ -123,12 +123,12 @@ let ArgToken = (() => {
123
123
  argDefault.setAttribute('aIndex', index);
124
124
  const childErrors = argDefault.lint(index, re);
125
125
  if (childErrors.length > 0) {
126
- errors.push(...childErrors);
126
+ Array.prototype.push.apply(errors, childErrors);
127
127
  }
128
128
  }
129
129
  const rules = ['no-ignored', 'no-arg'], { lintConfig } = index_1.default, { computeEditInfo } = lintConfig, rect = new rect_1.BoundingRect(this, start), s = rules.map(rule => lintConfig.getSeverity(rule, 'arg'));
130
130
  if (s[0] && rest.length > 0) {
131
- errors.push(...rest.map(child => {
131
+ Array.prototype.push.apply(errors, rest.map(child => {
132
132
  const e = (0, lint_1.generateForChild)(child, rect, rules[0], 'invisible-triple-braces', s[0]);
133
133
  e.startIndex--;
134
134
  e.startCol--;
@@ -12,6 +12,8 @@ const rect_1 = require("../lib/rect");
12
12
  const index_1 = __importDefault(require("../index"));
13
13
  const index_2 = require("./index");
14
14
  const atom_1 = require("./atom");
15
+ /* NOT FOR BROWSER ONLY */
16
+ const document_1 = require("../lib/document");
15
17
  const insecureStyle = /expression|(?:accelerator|-o-link(?:-source)?|-o-replace)\s*:|(?:url|src|image(?:-set)?)\s*\(|attr\s*\([^)]+[\s,]url/u, evil = /(?:^|\s|\*\/)(?:javascript|vbscript)(?:\W|$)/iu, complexTypes = new Set(['ext', 'arg', 'magic-word', 'template']), urlAttrs = new Set([
16
18
  'about',
17
19
  'property',
@@ -64,6 +66,7 @@ class AttributeToken extends index_2.Token {
64
66
  }
65
67
  else if (tag === 'gallery' && key === 'caption'
66
68
  || tag === 'ref' && key === 'details'
69
+ || (tag === 'mapframe' || tag === 'maplink') && key === 'text'
67
70
  || tag === 'choose' && (key === 'before' || key === 'after')) {
68
71
  const newConfig = {
69
72
  ...config,
@@ -207,6 +210,30 @@ class AttributeToken extends index_2.Token {
207
210
  if (s[1] && sharable_1.obsoleteAttrs[tag]?.has(name)) {
208
211
  errors.push((0, lint_1.generateForChild)(firstChild, rect, rules[1], 'obsolete-attribute', s[1]));
209
212
  }
213
+ /* NOT FOR BROWSER ONLY */
214
+ const rule = 'invalid-css', sError = lintConfig.getSeverity(rule), sWarn = lintConfig.getSeverity(rule, 'warn');
215
+ if (document_1.cssLSP
216
+ && (sError || sWarn)
217
+ && name === 'style'
218
+ && lastChild.length === 1 && lastChild.firstChild.type === 'text') {
219
+ const root = this.getRootNode(), textDoc = new document_1.EmbeddedCSSDocument(root, lastChild);
220
+ Array.prototype.push.apply(errors, document_1.cssLSP.doValidation(textDoc, textDoc.styleSheet)
221
+ .filter(({ code, severity }) => code !== 'css-ruleorselectorexpected' && code !== 'emptyRules'
222
+ && (severity === 1 ? sError : sWarn))
223
+ .map(({ range: { start: { line, character }, end }, message, severity, code }) => ({
224
+ code: code,
225
+ rule,
226
+ message,
227
+ severity: (severity === 1 ? sError : sWarn),
228
+ startLine: line,
229
+ startCol: character,
230
+ startIndex: root.indexFromPos(line, character),
231
+ endLine: end.line,
232
+ endCol: end.character,
233
+ endIndex: root.indexFromPos(end.line, end.character),
234
+ })));
235
+ }
236
+ /* NOT FOR BROWSER ONLY END */
210
237
  return errors;
211
238
  }
212
239
  }
@@ -176,7 +176,7 @@ class AttributesToken extends index_2.Token {
176
176
  const value = attr.getValue();
177
177
  return [attr, value === true ? '' : value];
178
178
  });
179
- errors.push(...pairs.map(([attr, value], i) => {
179
+ Array.prototype.push.apply(errors, pairs.map(([attr, value], i) => {
180
180
  const e = (0, lint_1.generateForChild)(attr, rect, rules[1], index_1.default.msg('duplicate-attribute', key), severity);
181
181
  if (computeEditInfo || fix) {
182
182
  const remove = (0, lint_1.fixByRemove)(e);
@@ -83,9 +83,12 @@ let ConverterToken = (() => {
83
83
  new converterRule_1.ConverterRuleToken(rules.join(';'), false, config, accum));
84
84
  }
85
85
  else {
86
- this.append(firstRuleToken, ...rules.slice(1)
87
- // @ts-expect-error abstract class
88
- .map((rule) => new converterRule_1.ConverterRuleToken(rule, true, config, accum)));
86
+ this.safeAppend([
87
+ firstRuleToken,
88
+ ...rules.slice(1)
89
+ // @ts-expect-error abstract class
90
+ .map((rule) => new converterRule_1.ConverterRuleToken(rule, true, config, accum)),
91
+ ]);
89
92
  }
90
93
  }
91
94
  /** @private */
@@ -2,7 +2,7 @@ import { Token } from './index';
2
2
  import type { LintError, Config } from '../base';
3
3
  import type { Title } from '../lib/title';
4
4
  import type { AtomToken, FileToken } from '../internal';
5
- export declare const galleryParams: Set<string>;
5
+ export declare const galleryParams: Set<string>, extensions: Set<string>;
6
6
  /**
7
7
  * image parameter
8
8
  *
@@ -15,6 +15,8 @@ export declare abstract class ImageParameterToken extends Token {
15
15
  abstract get nextSibling(): this | undefined;
16
16
  abstract get previousSibling(): AtomToken | this | undefined;
17
17
  get type(): 'image-parameter';
18
+ /** thumbnail / 缩略图 */
19
+ get thumb(): Title | undefined;
18
20
  /** image link / 图片链接 */
19
21
  get link(): string | Title | undefined;
20
22
  /** @param str 图片参数 */
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ImageParameterToken = exports.galleryParams = void 0;
6
+ exports.ImageParameterToken = exports.extensions = exports.galleryParams = void 0;
7
7
  const common_1 = require("@bhsd/common");
8
8
  const string_1 = require("../util/string");
9
9
  const lint_1 = require("../util/lint");
@@ -14,7 +14,7 @@ const index_2 = require("./index");
14
14
  const getUrlLikeRegex = (0, common_1.getRegex)(protocol => new RegExp(String.raw `^(?:${protocol}|//|\0\d+m\x7F)`, 'iu'));
15
15
  const getUrlRegex = (0, common_1.getRegex)(protocol => new RegExp(String.raw `^(?:(?:${protocol}|//)${string_1.extUrlCharFirst}|\0\d+m\x7F)${string_1.extUrlChar}$`, 'iu'));
16
16
  const getSyntaxRegex = (0, common_1.getRegex)(syntax => new RegExp(String.raw `^(\s*(?!\s))${syntax.replace('$1', '(.*)')}${syntax.endsWith('$1') ? '(?=$|\n)' : ''}(\s*)$`, 'u'));
17
- exports.galleryParams = new Set(['alt', 'link', 'lang', 'page', 'caption']);
17
+ exports.galleryParams = new Set(['alt', 'link', 'lang', 'page', 'caption']), exports.extensions = new Set(['tiff', 'tif', 'png', 'gif', 'jpg', 'jpeg', 'webp', 'xcf', 'pdf', 'svg', 'djvu']);
18
18
  /**
19
19
  * 获取网址
20
20
  * @param link 外链
@@ -44,7 +44,7 @@ function validate(key, val, config, halfParsed, ext) {
44
44
  else if (value.startsWith('[[') && value.endsWith(']]')) {
45
45
  value = value.slice(2, -2);
46
46
  }
47
- const title = index_1.default.normalizeTitle(value, 0, false, config, { halfParsed, decode: true, selfLink: true });
47
+ const title = index_1.default.normalizeTitle(value, 0, false, config, { halfParsed, decode: true, selfLink: true, page: '' });
48
48
  return title.valid && title;
49
49
  }
50
50
  case 'lang':
@@ -70,6 +70,12 @@ class ImageParameterToken extends index_2.Token {
70
70
  get type() {
71
71
  return 'image-parameter';
72
72
  }
73
+ /** thumbnail / 缩略图 */
74
+ get thumb() {
75
+ LINT: return this.name === 'manualthumb' // eslint-disable-line no-unused-labels
76
+ ? this.normalizeTitle(`File:${super.text().trim()}`, 6, { page: '' })
77
+ : undefined;
78
+ }
73
79
  /** image link / 图片链接 */
74
80
  get link() {
75
81
  LINT: { // eslint-disable-line no-unused-labels
@@ -183,6 +189,15 @@ class ImageParameterToken extends index_2.Token {
183
189
  }
184
190
  }
185
191
  }
192
+ else if (name === 'manualthumb') {
193
+ const rule = 'invalid-gallery', s = lintConfig.getSeverity(rule, 'thumb');
194
+ if (s && !this.querySelector('arg,magic-word,template')) {
195
+ const { valid, ns, extension, } = this.thumb;
196
+ if (!valid || ns !== 6 || !exports.extensions.has(extension)) {
197
+ errors.push((0, lint_1.generateForSelf)(this, { start }, rule, 'invalid-thumb', s));
198
+ }
199
+ }
200
+ }
186
201
  return errors;
187
202
  }
188
203
  }
@@ -17,6 +17,14 @@ export declare class Token extends AstElement {
17
17
  #private;
18
18
  get type(): TokenTypes;
19
19
  set type(value: TokenTypes);
20
+ /**
21
+ * page name
22
+ *
23
+ * 页面名称
24
+ * @since v1.29.0
25
+ */
26
+ get pageName(): string | undefined;
27
+ set pageName(value: string | undefined);
20
28
  /** @class */
21
29
  constructor(wikitext?: string, config?: Parser.Config, accum?: Token[], acceptable?: WikiParserAcceptable);
22
30
  /**
package/dist/src/index.js CHANGED
@@ -55,9 +55,6 @@ const debug_1 = require("../util/debug");
55
55
  const index_1 = __importDefault(require("../index"));
56
56
  const element_1 = require("../lib/element");
57
57
  const text_1 = require("../lib/text");
58
- /* NOT FOR BROWSER ONLY */
59
- const document_1 = require("../lib/document");
60
- const lsp_1 = require("../lib/lsp");
61
58
  const lintSelectors = ['category', 'html-attr#id,ext-attr#id,table-attr#id'];
62
59
  /**
63
60
  * base class for all tokens
@@ -75,12 +72,31 @@ class Token extends element_1.AstElement {
75
72
  #include;
76
73
  #built = false;
77
74
  #string;
75
+ #pageName;
78
76
  get type() {
79
77
  return this.#type;
80
78
  }
81
79
  set type(value) {
82
80
  this.#type = value;
83
81
  }
82
+ /**
83
+ * page name
84
+ *
85
+ * 页面名称
86
+ * @since v1.29.0
87
+ */
88
+ get pageName() {
89
+ return this.getRootNode().#pageName;
90
+ }
91
+ set pageName(value) {
92
+ if (value) {
93
+ const title = this.normalizeTitle(value, 0, { temporary: true, page: '' });
94
+ this.#pageName = title.valid ? title.title : undefined;
95
+ }
96
+ else {
97
+ this.#pageName = value === '' ? '' : undefined;
98
+ }
99
+ }
84
100
  /** @class */
85
101
  constructor(wikitext, config = index_1.default.getConfig(), accum = [], acceptable) {
86
102
  super();
@@ -255,7 +271,7 @@ class Token extends element_1.AstElement {
255
271
  */
256
272
  #parseLinks(tidy) {
257
273
  const { parseLinks } = require('../parser/links');
258
- this.setText(parseLinks(this.firstChild.toString(), this.#config, this.#accum, tidy));
274
+ this.setText(parseLinks(this.firstChild.toString(), this.#config, this.#accum, this.pageName, tidy));
259
275
  }
260
276
  /**
261
277
  * 解析单引号
@@ -353,7 +369,7 @@ class Token extends element_1.AstElement {
353
369
  }
354
370
  /** @private */
355
371
  normalizeTitle(title, defaultNs = 0, opt) {
356
- return index_1.default.normalizeTitle(title, defaultNs, this.#include, this.#config, opt);
372
+ return index_1.default.normalizeTitle(title, defaultNs, this.getAttribute('include'), this.#config, { page: this.pageName, ...opt });
357
373
  }
358
374
  /** @private */
359
375
  inTableAttrs() {
@@ -464,29 +480,6 @@ class Token extends element_1.AstElement {
464
480
  delete e.suggestions;
465
481
  }
466
482
  }
467
- /* NOT FOR BROWSER ONLY */
468
- }
469
- else if ((0, lsp_1.isAttr)(this, true)) {
470
- const rule = 'invalid-css', s = lintConfig.getSeverity(rule), sWarn = lintConfig.getSeverity(rule, 'warn');
471
- if (s || sWarn) {
472
- const root = this.getRootNode(), textDoc = new document_1.EmbeddedCSSDocument(root, this);
473
- errors.push(...document_1.cssLSP.doValidation(textDoc, textDoc.styleSheet)
474
- .filter(({ code, severity }) => code !== 'css-ruleorselectorexpected' && code !== 'emptyRules'
475
- && (severity === 1 ? s : sWarn))
476
- .map(({ range: { start: { line, character }, end }, message, severity, code }) => ({
477
- code: code,
478
- rule,
479
- message,
480
- severity: (severity === 1 ? s : sWarn),
481
- startLine: line,
482
- startCol: character,
483
- startIndex: root.indexFromPos(line, character),
484
- endLine: end.line,
485
- endCol: end.character,
486
- endIndex: root.indexFromPos(end.line, end.character),
487
- })));
488
- }
489
- /* NOT FOR BROWSER ONLY END */
490
483
  }
491
484
  return errors;
492
485
  }
@@ -14,7 +14,7 @@ const frame = new Map([
14
14
  ['frameless', 'Frameless'],
15
15
  ['framed', 'Frame'],
16
16
  ['thumbnail', 'Thumb'],
17
- ]), argTypes = new Set(['arg']), transclusion = new Set(['template', 'magic-word']), horizAlign = new Set(['left', 'right', 'center', 'none']), vertAlign = new Set(['baseline', 'sub', 'super', 'top', 'text-top', 'middle', 'bottom', 'text-bottom']), extensions = new Set(['tiff', 'tif', 'png', 'gif', 'jpg', 'jpeg', 'webp', 'xcf', 'pdf', 'svg', 'djvu']);
17
+ ]), argTypes = new Set(['arg']), transclusion = new Set(['template', 'magic-word']), horizAlign = new Set(['left', 'right', 'center', 'none']), vertAlign = new Set(['baseline', 'sub', 'super', 'top', 'text-top', 'middle', 'bottom', 'text-bottom']);
18
18
  /**
19
19
  * a more sophisticated string-explode function
20
20
  * @param str string to be exploded
@@ -82,7 +82,7 @@ class FileToken extends base_1.LinkBaseToken {
82
82
  const errors = super.lint(start, re), args = filterArgs(this.getAllArgs(), argTypes), keys = [...new Set(args.map(({ name }) => name))], frameKeys = keys.filter(key => frame.has(key)), horizAlignKeys = keys.filter(key => horizAlign.has(key)), vertAlignKeys = keys.filter(key => vertAlign.has(key)), [fr] = frameKeys, unscaled = fr === 'framed' || fr === 'manualthumb', rect = new rect_1.BoundingRect(this, start), { lintConfig } = index_1.default, { computeEditInfo, fix } = lintConfig, { ns, extension, } = this.getAttribute('title'), { firstChild } = this;
83
83
  let rule = 'nested-link', s = lintConfig.getSeverity(rule, 'file');
84
84
  if (s
85
- && extensions.has(extension)
85
+ && imageParameter_1.extensions.has(extension)
86
86
  && this.closest('ext-link-text')
87
87
  && this.getValue('link')?.trim() !== '') {
88
88
  const e = (0, lint_1.generateForSelf)(this, rect, rule, 'link-in-extlink', s);
@@ -157,22 +157,22 @@ class FileToken extends base_1.LinkBaseToken {
157
157
  ];
158
158
  }
159
159
  if (relevantArgs.length > 1) {
160
- let severity = !isCaption || !extension || extensions.has(extension);
160
+ let severity = !isCaption || !extension || imageParameter_1.extensions.has(extension);
161
161
  if (isCaption && severity) {
162
162
  const plainArgs = filterArgs(relevantArgs, transclusion);
163
163
  severity = plainArgs.length > 1 && ((arg) => plainArgs.includes(arg));
164
164
  }
165
- errors.push(...generate(relevantArgs, 'duplicate', key, severity));
165
+ Array.prototype.push.apply(errors, generate(relevantArgs, 'duplicate', key, severity));
166
166
  }
167
167
  }
168
168
  if (frameKeys.length > 1) {
169
- errors.push(...generate(args.filter(({ name }) => frame.has(name)), 'conflicting', 'frame'));
169
+ Array.prototype.push.apply(errors, generate(args.filter(({ name }) => frame.has(name)), 'conflicting', 'frame'));
170
170
  }
171
171
  if (horizAlignKeys.length > 1) {
172
- errors.push(...generate(args.filter(({ name }) => horizAlign.has(name)), 'conflicting', 'horizontal-alignment'));
172
+ Array.prototype.push.apply(errors, generate(args.filter(({ name }) => horizAlign.has(name)), 'conflicting', 'horizontal-alignment'));
173
173
  }
174
174
  if (vertAlignKeys.length > 1) {
175
- errors.push(...generate(args.filter(({ name }) => vertAlign.has(name)), 'conflicting', 'vertical-alignment'));
175
+ Array.prototype.push.apply(errors, generate(args.filter(({ name }) => vertAlign.has(name)), 'conflicting', 'vertical-alignment'));
176
176
  }
177
177
  return errors;
178
178
  }
@@ -91,7 +91,7 @@ let GalleryImageToken = (() => {
91
91
  /** @private */
92
92
  getTitle(temporary) {
93
93
  const imagemap = this.type === 'imagemap-image';
94
- return this.normalizeTitle(this.firstChild.toString(), imagemap ? 0 : 6, { halfParsed: true, temporary, decode: !imagemap });
94
+ return this.normalizeTitle(this.firstChild.toString(), imagemap ? 0 : 6, { halfParsed: true, temporary, decode: !imagemap, page: '' });
95
95
  }
96
96
  /** 判定无效的图片 */
97
97
  #lint() {
@@ -31,7 +31,7 @@ class RedirectTargetToken extends base_1.LinkBaseToken {
31
31
  }
32
32
  /** @private */
33
33
  getTitle() {
34
- return this.normalizeTitle(this.firstChild.toString(), 0, { halfParsed: true, decode: true });
34
+ return this.normalizeTitle(this.firstChild.toString(), 0, { halfParsed: true, decode: true, page: '' });
35
35
  }
36
36
  /** @private */
37
37
  lint(start = this.getAbsoluteIndex()) {
@@ -42,7 +42,7 @@ class GalleryToken extends index_2.MultiLineToken {
42
42
  * @param file 文件名
43
43
  */
44
44
  #checkFile(file) {
45
- return this.normalizeTitle(file, 6, { halfParsed: true, temporary: true, decode: true }).valid;
45
+ return this.normalizeTitle(file, 6, { halfParsed: true, temporary: true, decode: true, page: '' }).valid;
46
46
  }
47
47
  /** @private */
48
48
  lint(start = this.getAbsoluteIndex(), re) {
@@ -83,7 +83,7 @@ class GalleryToken extends index_2.MultiLineToken {
83
83
  else if (type !== 'noinclude' && type !== 'text') {
84
84
  const childErrors = child.lint(start, re);
85
85
  if (childErrors.length > 0) {
86
- errors.push(...childErrors);
86
+ Array.prototype.push.apply(errors, childErrors);
87
87
  }
88
88
  }
89
89
  start += length + 1;
@@ -35,7 +35,7 @@ class ImagemapToken extends index_2.MultiLineToken {
35
35
  //
36
36
  }
37
37
  else if (first) {
38
- const pipe = line.indexOf('|'), file = pipe === -1 ? line : line.slice(0, pipe), { valid, ns, } = this.normalizeTitle(file, 0, { halfParsed: true, temporary: true });
38
+ const pipe = line.indexOf('|'), file = pipe === -1 ? line : line.slice(0, pipe), { valid, ns, } = this.normalizeTitle(file, 0, { halfParsed: true, temporary: true, page: '' });
39
39
  if (valid
40
40
  && ns === 6) {
41
41
  // @ts-expect-error abstract class
@@ -56,8 +56,7 @@ class ImagemapToken extends index_2.MultiLineToken {
56
56
  const i = line.indexOf('['), substr = line.slice(i), mtIn = /^\[\[([^|]+)(?:\|([^\]]*))?\]\][\w\s]*$/u
57
57
  .exec(substr);
58
58
  if (mtIn) {
59
- if (this.normalizeTitle(mtIn[1], 0, { halfParsed: true, temporary: true, selfLink: true })
60
- .valid) {
59
+ if (this.normalizeTitle(mtIn[1], 0, { halfParsed: true, temporary: true, selfLink: true, page: '' }).valid) {
61
60
  // @ts-expect-error abstract class
62
61
  super.insertAt(new imagemapLink_1.ImagemapLinkToken(line.slice(0, i), mtIn.slice(1), substr.slice(substr.indexOf(']]') + 2), config, accum));
63
62
  continue;
@@ -85,7 +84,7 @@ class ImagemapToken extends index_2.MultiLineToken {
85
84
  const errors = super.lint(start, re), rect = new rect_1.BoundingRect(this, start), { childNodes, image } = this, rule = 'invalid-imagemap', { lintConfig } = index_1.default, s = lintConfig.getSeverity(rule, image ? 'link' : 'image');
86
85
  if (s) {
87
86
  if (image) {
88
- errors.push(...childNodes.filter(child => {
87
+ Array.prototype.push.apply(errors, childNodes.filter(child => {
89
88
  const str = child.toString().trim();
90
89
  return child.is('noinclude')
91
90
  && str && !str.startsWith('#');
@@ -19,7 +19,7 @@ class ParamTagToken extends index_2.MultiLineToken {
19
19
  constructor(include, wikitext, config = index_1.default.getConfig(), accum = [], acceptable) {
20
20
  super(undefined, config, accum, {});
21
21
  if (wikitext) {
22
- this.append(...wikitext.split('\n')
22
+ this.safeAppend(wikitext.split('\n')
23
23
  .map(line => acceptable ? line : (0, commentAndExt_1.parseCommentAndExt)(line, config, accum, include))
24
24
  // @ts-expect-error abstract class
25
25
  .map((line) => new paramLine_1.ParamLineToken(line, config, accum, {})));
@@ -54,7 +54,7 @@ class ParamTagToken extends index_2.MultiLineToken {
54
54
  else {
55
55
  const childErrors = child.lint(start, false);
56
56
  if (childErrors.length > 0) {
57
- errors.push(...childErrors);
57
+ Array.prototype.push.apply(errors, childErrors);
58
58
  }
59
59
  }
60
60
  }