wikiparser-node 1.21.3 → 1.22.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.
Files changed (67) hide show
  1. package/README.md +6 -0
  2. package/bundle/bundle-es8.min.js +24 -24
  3. package/bundle/bundle-lsp.min.js +26 -26
  4. package/bundle/bundle.min.js +24 -24
  5. package/config/default.json +17 -11
  6. package/config/enwiki.json +1 -1
  7. package/config/jawiki.json +1 -1
  8. package/config/minimum.json +3 -3
  9. package/config/moegirl.json +154 -15
  10. package/config/zhwiki.json +1 -1
  11. package/dist/addon/token.js +3 -0
  12. package/dist/base.d.mts +10 -8
  13. package/dist/base.d.ts +10 -8
  14. package/dist/bin/config.js +23 -11
  15. package/dist/index.d.ts +31 -2
  16. package/dist/index.js +27 -1
  17. package/dist/lib/document.js +1 -1
  18. package/dist/lib/element.d.ts +4 -4
  19. package/dist/lib/element.js +7 -7
  20. package/dist/lib/lintConfig.js +123 -6
  21. package/dist/lib/lsp.d.ts +7 -7
  22. package/dist/lib/lsp.js +36 -23
  23. package/dist/lib/node.d.ts +1 -1
  24. package/dist/lib/node.js +1 -1
  25. package/dist/lib/range.d.ts +2 -2
  26. package/dist/lib/range.js +10 -4
  27. package/dist/lib/text.js +72 -62
  28. package/dist/lib/title.d.ts +11 -4
  29. package/dist/lib/title.js +16 -6
  30. package/dist/mixin/readOnly.js +3 -1
  31. package/dist/parser/commentAndExt.js +6 -4
  32. package/dist/parser/converter.js +1 -1
  33. package/dist/parser/selector.js +11 -9
  34. package/dist/src/arg.js +11 -10
  35. package/dist/src/attribute.js +31 -19
  36. package/dist/src/attributes.js +10 -8
  37. package/dist/src/commented.js +1 -1
  38. package/dist/src/converterRule.js +2 -2
  39. package/dist/src/gallery.js +26 -19
  40. package/dist/src/heading.js +32 -36
  41. package/dist/src/html.js +64 -51
  42. package/dist/src/index.d.ts +2 -2
  43. package/dist/src/index.js +10 -8
  44. package/dist/src/link/base.d.ts +1 -1
  45. package/dist/src/link/base.js +22 -15
  46. package/dist/src/link/file.js +22 -14
  47. package/dist/src/link/index.d.ts +1 -1
  48. package/dist/src/link/index.js +1 -1
  49. package/dist/src/magicLink.js +16 -12
  50. package/dist/src/nowiki/index.js +1 -1
  51. package/dist/src/nowiki/quote.js +19 -38
  52. package/dist/src/table/index.js +33 -27
  53. package/dist/src/table/td.d.ts +1 -1
  54. package/dist/src/table/td.js +27 -25
  55. package/dist/src/table/trBase.js +13 -10
  56. package/dist/src/tagPair/ext.js +6 -3
  57. package/dist/src/tagPair/index.js +2 -3
  58. package/dist/src/transclude.d.ts +15 -1
  59. package/dist/src/transclude.js +43 -13
  60. package/dist/util/lint.js +3 -5
  61. package/dist/util/sharable.js +1 -1
  62. package/dist/util/sharable.mjs +2 -2
  63. package/extensions/dist/base.js +8 -1
  64. package/extensions/typings.d.ts +2 -1
  65. package/i18n/zh-hans.json +1 -1
  66. package/i18n/zh-hant.json +1 -1
  67. package/package.json +8 -3
package/dist/lib/lsp.js CHANGED
@@ -18,7 +18,7 @@ const path_1 = __importDefault(require("path"));
18
18
  const util_1 = __importDefault(require("util"));
19
19
  const child_process_1 = require("child_process");
20
20
  const crypto_1 = require("crypto");
21
- const stylelint_1 = require("@bhsd/common/dist/stylelint");
21
+ const stylelint_util_1 = require("@bhsd/stylelint-util");
22
22
  const document_1 = require("./document");
23
23
  /** @see https://www.npmjs.com/package/stylelint-config-recommended */
24
24
  const cssRules = {
@@ -227,14 +227,16 @@ const adjustPos = (height, width, line, column) => {
227
227
  * Get the position of a Stylelint error.
228
228
  * @param rect bounding client rect of the token
229
229
  * @param bottom bottom of the style block
230
- * @param line line number
231
- * @param column column number
230
+ * @param lineOrCode line number or code string
231
+ * @param columnOrOffset column number or offset in the code string
232
232
  */
233
- const getStylelintPos = (rect, bottom, line, column) => {
233
+ const getStylelintPos = (rect, bottom, lineOrCode, columnOrOffset) => {
234
234
  const { top, left, height, width } = rect, start = bottom - height - 1;
235
- line -= start;
236
- [line, column] = adjustPos(height, width, line, column);
237
- return (0, lint_1.getEndPos)(top, left, line, column);
235
+ if (typeof lineOrCode === 'number') {
236
+ return (0, lint_1.getEndPos)(top, left, ...adjustPos(height, width, lineOrCode - start, columnOrOffset));
237
+ }
238
+ const lines = lineOrCode.slice(0, columnOrOffset).split(/\r?\n/u);
239
+ return getStylelintPos(rect, bottom, lines.length, lines.at(-1).length);
238
240
  };
239
241
  /**
240
242
  * Convert LilyPond errors to VSCode diagnostics.
@@ -534,7 +536,7 @@ class LanguageService {
534
536
  *
535
537
  * 提供自动补全
536
538
  * @param text source Wikitext / 源代码
537
- * @param position 位置
539
+ * @param position position / 位置
538
540
  */
539
541
  async provideCompletionItems(text, position) {
540
542
  const { re, allTags, functions, switches, 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) ?? ''), [, , iAlias = {}, sAlias = {}] = this.config.doubleUnderscore;
@@ -762,7 +764,7 @@ class LanguageService {
762
764
  if (tokens.length === 0) {
763
765
  return [];
764
766
  }
765
- const cssErrors = await (0, stylelint_1.styleLint)((await document_1.stylelint), tokens.map(({ type, tag, lastChild }, i) => `${type === 'ext-attr' ? 'div' : tag}#${i}{\n${(0, common_1.sanitizeInlineStyle)(lastChild.toString())}\n}`).join('\n'), cssRules);
767
+ 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 (0, stylelint_util_1.styleLint)((await document_1.stylelint), code, cssRules);
766
768
  if (cssErrors.length === 0) {
767
769
  return [];
768
770
  }
@@ -772,7 +774,7 @@ class LanguageService {
772
774
  acc += height + 2;
773
775
  return acc;
774
776
  });
775
- return cssErrors.map(({ rule, text: msg, severity, line, column, endLine = line, endColumn = column, }) => {
777
+ return cssErrors.map(({ rule, text: msg, severity, line, column, endLine = line, endColumn = column, fix, }) => {
776
778
  const i = bottoms.findIndex(bottom => bottom >= line);
777
779
  return {
778
780
  range: {
@@ -783,6 +785,21 @@ class LanguageService {
783
785
  source: 'Stylelint',
784
786
  code: rule,
785
787
  message: msg.slice(0, msg.lastIndexOf('(') - 1),
788
+ ...fix
789
+ ? {
790
+ data: [
791
+ {
792
+ range: {
793
+ start: getStylelintPos(rects[i], bottoms[i], code, fix.range[0]),
794
+ end: getStylelintPos(rects[i], bottoms[i], code, fix.range[1]),
795
+ },
796
+ newText: fix.text,
797
+ title: `Fix: ${rule}`,
798
+ fix: true,
799
+ },
800
+ ],
801
+ }
802
+ : {},
786
803
  };
787
804
  });
788
805
  })() :
@@ -1056,7 +1073,7 @@ class LanguageService {
1056
1073
  *
1057
1074
  * 提供引用
1058
1075
  * @param text source Wikitext / 源代码
1059
- * @param position 位置
1076
+ * @param position position / 位置
1060
1077
  */
1061
1078
  async provideReferences(text, position) {
1062
1079
  const root = await this.#queue(text), { offsetNode, offset } = caretPositionFromWord(root, this.#text, position), element = offsetNode.type === 'text' ? offsetNode.parentNode : offsetNode, node = offset === 0 && (element.type === 'ext-attr-dirty' || element.type === 'html-attr-dirty')
@@ -1077,7 +1094,7 @@ class LanguageService {
1077
1094
  *
1078
1095
  * 提供定义
1079
1096
  * @param text source Wikitext / 源代码
1080
- * @param position 位置
1097
+ * @param position position / 位置
1081
1098
  */
1082
1099
  async provideDefinition(text, position) {
1083
1100
  const root = await this.#queue(text), node = root.elementFromPoint(position.character, position.line), ext = node.is('ext') && node.name === 'ref'
@@ -1098,7 +1115,7 @@ class LanguageService {
1098
1115
  *
1099
1116
  * 提供变量更名准备
1100
1117
  * @param text source Wikitext / 源代码
1101
- * @param position 位置
1118
+ * @param position position / 位置
1102
1119
  */
1103
1120
  async resolveRenameLocation(text, position) {
1104
1121
  const root = await this.#queue(text), node = root.elementFromPoint(position.character, position.line), { type } = node, refName = getRefName(node), refGroup = getRefGroup(node);
@@ -1113,7 +1130,7 @@ class LanguageService {
1113
1130
  *
1114
1131
  * 变量更名
1115
1132
  * @param text source Wikitext / 源代码
1116
- * @param position 位置
1133
+ * @param position position / 位置
1117
1134
  * @param newName new name / 新名称
1118
1135
  */
1119
1136
  async provideRenameEdits(text, position, newName) {
@@ -1159,7 +1176,7 @@ class LanguageService {
1159
1176
  *
1160
1177
  * 提供悬停信息
1161
1178
  * @param text source Wikitext / 源代码
1162
- * @param position 位置
1179
+ * @param position position / 位置
1163
1180
  */
1164
1181
  async provideHover(text, position) {
1165
1182
  /* istanbul ignore next */
@@ -1255,7 +1272,7 @@ class LanguageService {
1255
1272
  *
1256
1273
  * 提供魔术字帮助
1257
1274
  * @param text source Wikitext / 源代码
1258
- * @param position 位置
1275
+ * @param position position / 位置
1259
1276
  */
1260
1277
  async provideSignatureHelp(text, position) {
1261
1278
  /* istanbul ignore next */
@@ -1396,19 +1413,15 @@ class LanguageService {
1396
1413
  * @since v1.18.1
1397
1414
  */
1398
1415
  async setTargetWikipedia(wiki) {
1399
- const mt = /^https?:\/\/([^./]+)\.wikipedia\.org/iu.exec(wiki);
1400
- if (!mt) {
1401
- throw new RangeError('Invalid Wikipedia URL!');
1402
- }
1403
- const site = `${mt[1].toLowerCase()}wiki`;
1416
+ const [site, host] = index_1.default.getWMFSite(wiki);
1404
1417
  try {
1405
1418
  const config = require(path_1.default.join('..', '..', 'config', site));
1406
1419
  this.config = index_1.default.getConfig(config);
1407
1420
  }
1408
1421
  catch {
1409
- this.config = await index_1.default.fetchConfig(site, `${mt[0]}/w`);
1422
+ this.config = await index_1.default.fetchConfig(site, `${host}/w`);
1410
1423
  }
1411
- Object.assign(this.config, { articlePath: `${mt[0]}/wiki/` });
1424
+ Object.assign(this.config, { articlePath: `${host}/wiki/` });
1412
1425
  }
1413
1426
  }
1414
1427
  exports.LanguageService = LanguageService;
@@ -197,7 +197,7 @@ export declare abstract class AstNode implements AstNodeBase {
197
197
  * 添加事件监听
198
198
  * @param types event type / 事件类型
199
199
  * @param listener listener function / 监听函数
200
- * @param options 选项
200
+ * @param options options / 选项
201
201
  * @param options.once to be executed only once / 仅执行一次
202
202
  */
203
203
  addEventListener(types: string | string[], listener: (...args: any[]) => void, options?: {
package/dist/lib/node.js CHANGED
@@ -514,7 +514,7 @@ let AstNode = (() => {
514
514
  * 添加事件监听
515
515
  * @param types event type / 事件类型
516
516
  * @param listener listener function / 监听函数
517
- * @param options 选项
517
+ * @param options options / 选项
518
518
  * @param options.once to be executed only once / 仅执行一次
519
519
  */
520
520
  addEventListener(types, listener, options) {
@@ -99,7 +99,7 @@ export declare class AstRange {
99
99
  *
100
100
  * 比较端点和Range的位置
101
101
  * @param referenceNode node container / 端点容器
102
- * @param offset 端点位置
102
+ * @param offset node offset / 端点位置
103
103
  * @throws `RangeError` 不在同一个文档
104
104
  */
105
105
  comparePoint(referenceNode: AstNodes, offset: number): -1 | 0 | 1;
@@ -108,7 +108,7 @@ export declare class AstRange {
108
108
  *
109
109
  * 端点是否在Range中
110
110
  * @param referenceNode node container / 端点容器
111
- * @param offset 端点位置
111
+ * @param offset node offset / 端点位置
112
112
  */
113
113
  isPointInRange(referenceNode: AstNodes, offset: number): boolean;
114
114
  /**
package/dist/lib/range.js CHANGED
@@ -6,8 +6,13 @@ const constants_1 = require("../util/constants");
6
6
  * 计算绝对位置
7
7
  * @param referenceNode 容器
8
8
  * @param offset 相对位置
9
+ * @param end 是否是终点
9
10
  */
10
- const getIndex = (referenceNode, offset) => referenceNode.getAbsoluteIndex() + referenceNode.getRelativeIndex(offset);
11
+ const getIndex = (referenceNode, offset, end) => {
12
+ end &&= referenceNode.type !== 'text';
13
+ return referenceNode.getAbsoluteIndex() + referenceNode.getRelativeIndex(offset - (end ? 1 : 0))
14
+ + (end ? referenceNode.childNodes[offset - 1].toString().length : 0);
15
+ };
11
16
  /**
12
17
  * 获取父节点或抛出错误
13
18
  * @param node 参考节点
@@ -66,7 +71,8 @@ class AstRange {
66
71
  }
67
72
  /** end character index / 终点绝对位置 */
68
73
  get endIndex() {
69
- return getIndex(this.endContainer, this.endOffset);
74
+ const { endContainer, endOffset } = this;
75
+ return getIndex(endContainer, endOffset, this.#startContainer ? !this.collapsed : endOffset > 0);
70
76
  }
71
77
  /** end position / 终点行列位置 */
72
78
  get endPos() {
@@ -277,7 +283,7 @@ class AstRange {
277
283
  *
278
284
  * 比较端点和Range的位置
279
285
  * @param referenceNode node container / 端点容器
280
- * @param offset 端点位置
286
+ * @param offset node offset / 端点位置
281
287
  * @throws `RangeError` 不在同一个文档
282
288
  */
283
289
  comparePoint(referenceNode, offset) {
@@ -297,7 +303,7 @@ class AstRange {
297
303
  *
298
304
  * 端点是否在Range中
299
305
  * @param referenceNode node container / 端点容器
300
- * @param offset 端点位置
306
+ * @param offset node offset / 端点位置
301
307
  */
302
308
  isPointInRange(referenceNode, offset) {
303
309
  return this.comparePoint(referenceNode, offset) === 0;
package/dist/lib/text.js CHANGED
@@ -63,16 +63,6 @@ const errorSyntaxUrl = new RegExp(source, 'giu'), noLinkTypes = new Set(['attr-v
63
63
  '{': /[{}]/u,
64
64
  ']': /[[\]](?=[^[\]]*$)/u,
65
65
  '}': /[{}](?=[^{}]*$)/u,
66
- }, ruleMap = {
67
- '<': 'tag-like',
68
- '[': 'lonely-bracket',
69
- '{': 'lonely-bracket',
70
- ']': 'lonely-bracket',
71
- '}': 'lonely-bracket',
72
- h: 'lonely-http',
73
- r: 'lonely-http',
74
- p: 'lonely-http',
75
- i: 'lonely-http',
76
66
  }, disallowedTags = new Set([
77
67
  'html',
78
68
  'base',
@@ -210,13 +200,9 @@ let AstText = (() => {
210
200
  throw new Error('An isolated text node cannot be linted!');
211
201
  }
212
202
  const { type, name, parentNode: grandparent } = parentNode;
213
- let isHtmlAttrVal = false;
214
203
  if (type === 'attr-value') {
215
- const { type: grandType, name: grandName, tag } = grandparent;
216
- if (grandType !== 'ext-attr') {
217
- isHtmlAttrVal = true;
218
- }
219
- else if (tag === 'ref' && (grandName === 'name' || grandName === 'extends' || grandName === 'follow')
204
+ const { name: grandName, tag } = grandparent;
205
+ if (tag === 'ref' && (grandName === 'name' || grandName === 'extends' || grandName === 'follow')
220
206
  || grandName === 'group' && (tag === 'ref' || tag === 'references')
221
207
  || tag === 'choose' && (grandName === 'before' || grandName === 'after')) {
222
208
  return [];
@@ -226,7 +212,7 @@ let AstText = (() => {
226
212
  || type === 'ext-link-url'
227
213
  || type === 'ext-link-text'
228
214
  || type === 'image-parameter' && name === 'link'
229
- || isHtmlAttrVal
215
+ || type === 'attr-value'
230
216
  ? errorSyntaxUrl
231
217
  : errorSyntax;
232
218
  if (data.search(errorRegex) === -1) {
@@ -252,72 +238,95 @@ let AstText = (() => {
252
238
  error = error.slice(length);
253
239
  }
254
240
  error = error.toLowerCase();
255
- const [char] = error, magicLink = char === 'r' || char === 'p' || char === 'i';
241
+ const [char] = error, magicLink = char === 'r' || char === 'p' || char === 'i', lbrace = char === '{', rbrace = char === '}', lbrack = char === '[', rbrack = char === ']';
256
242
  let { length } = error;
257
243
  if (char === '<' && !tags.has(tag.toLowerCase())
258
- || char === '[' && type === 'ext-link-text' && (/&(?:rbrack|#93|#x5[Dd];);/u.test(data.slice(index + 1))
244
+ || lbrack && type === 'ext-link-text' && (/&(?:rbrack|#93|#x5[Dd];);/u.test(data.slice(index + 1))
259
245
  || nextSibling?.is('ext') && nextName === 'nowiki'
260
246
  && nextSibling.innerText?.includes(']'))
261
247
  || magicLink && (!parentNode.isPlain() || noLinkTypes.has(type))) {
262
248
  continue;
263
249
  }
264
- else if (char === ']' && (index || length > 1)) {
250
+ else if (rbrack && (index || length > 1)) {
265
251
  errorRegex.lastIndex--;
266
252
  }
267
- let startIndex = start + index, endIndex = startIndex + length;
268
- const nextChar = rootStr[endIndex], previousChar = rootStr[startIndex - 1];
269
- let severity = length > 1 && !(char === '<' && (/^<\s/u.test(error) || !/[\s/>]/u.test(nextChar ?? '') || disallowedTags.has(tag))
270
- || isHtmlAttrVal && (char === '[' || char === ']')
271
- || magicLink && type === 'parameter-value'
272
- || /^(?:rfc|pmid|isbn)$/iu.test(error))
273
- || char === '{' && (nextChar === char || previousChar === '-' && variants.length > 0)
274
- || char === '}' && (previousChar === char || nextChar === '-' && variants.length > 0)
275
- || char === '[' && (type === 'ext-link-text' || nextType === 'free-ext-link' && !data.slice(index + 1).trim())
276
- || char === ']' && previousType === 'free-ext-link'
277
- && !data.slice(0, index).includes(']')
278
- ? 'error'
279
- : 'warning';
280
- const leftBracket = char === '{' || char === '[', rightBracket = char === ']' || char === '}';
281
- if (severity === 'warning' && (leftBracket || rightBracket)) {
282
- const regex = regexes[char], remains = leftBracket ? data.slice(index + 1) : data.slice(0, index);
283
- if (char === '{' && regex.exec(remains)?.[0] === '}'
284
- || char === '}' && regex.exec(remains)?.[0] === '{') {
285
- continue;
286
- }
287
- else if (!remains.includes(char)) {
288
- const sibling = leftBracket ? 'nextSibling' : 'previousSibling';
289
- let cur = this[sibling];
290
- while (cur && (cur.type !== 'text' || !regex.test(cur.data))) {
291
- cur = cur[sibling];
292
- }
293
- if (cur && regex.exec(cur.data)[0] !== char) {
294
- continue;
295
- }
296
- }
297
- }
253
+ // Rule & Severity
254
+ let startIndex = start + index, endIndex = startIndex + length, rule, severity;
255
+ const nextChar = rootStr[endIndex], previousChar = rootStr[startIndex - 1], leftBracket = lbrace || lbrack, lConverter = lbrace && previousChar === '-' && variants.length > 0, rConverter = rbrace && nextChar === '-' && variants.length > 0, brokenExtLink = lbrack && nextType === 'free-ext-link' && !data.slice(index + 1).trim()
256
+ || rbrack && previousType === 'free-ext-link'
257
+ && !data.slice(0, index).includes(']');
298
258
  if (magicLink) {
259
+ rule = 'lonely-http';
299
260
  error = error.toUpperCase();
261
+ severity = index_1.default.lintConfig.getSeverity(rule, error);
300
262
  }
301
- else if (error === '{' && previousChar === '-' && severity === 'error') {
302
- severity = 'warning';
303
- if (index > 0) {
263
+ else if (char === '<') {
264
+ rule = 'tag-like';
265
+ let key;
266
+ if (/^<\s/u.test(error) || !/[\s/>]/u.test(nextChar ?? '')) {
267
+ key = 'invalid';
268
+ }
269
+ else if (disallowedTags.has(tag)) {
270
+ key = 'disallowed';
271
+ }
272
+ severity = index_1.default.lintConfig.getSeverity(rule, key);
273
+ }
274
+ else if (lConverter || rConverter) {
275
+ rule = 'lonely-bracket';
276
+ severity = index_1.default.lintConfig.getSeverity(rule, 'converter');
277
+ if (lConverter && index > 0) {
304
278
  error = '-{';
305
279
  index--;
306
280
  startIndex--;
307
281
  length = 2;
308
282
  }
309
- }
310
- else if (error === '}' && nextChar === '-' && severity === 'error') {
311
- severity = 'warning';
312
- if (index < data.length - 1) {
283
+ else if (rConverter && index < data.length - 1) {
313
284
  error = '}-';
314
285
  endIndex++;
315
286
  length = 2;
316
287
  }
317
288
  }
289
+ else if (brokenExtLink) {
290
+ rule = 'lonely-bracket';
291
+ severity = index_1.default.lintConfig.getSeverity(rule, 'extLink');
292
+ }
293
+ else if (leftBracket || rbrace || rbrack) {
294
+ rule = 'lonely-bracket';
295
+ if (length > 1 || lbrace && nextChar === char || rbrace && previousChar === char) {
296
+ severity = index_1.default.lintConfig.getSeverity(rule, 'double');
297
+ }
298
+ else {
299
+ if (!lbrack || type !== 'ext-link-text') {
300
+ const regex = regexes[char], remains = leftBracket ? data.slice(index + 1) : data.slice(0, index);
301
+ if (lbrace && regex.exec(remains)?.[0] === '}'
302
+ || rbrace && regex.exec(remains)?.[0] === '{') {
303
+ continue;
304
+ }
305
+ else if (!remains.includes(char)) {
306
+ const sibling = leftBracket ? 'nextSibling' : 'previousSibling';
307
+ let cur = this[sibling];
308
+ while (cur && (cur.type !== 'text' || !regex.test(cur.data))) {
309
+ cur = cur[sibling];
310
+ }
311
+ if (cur && regex.exec(cur.data)[0] !== char) {
312
+ continue;
313
+ }
314
+ }
315
+ }
316
+ severity = index_1.default.lintConfig.getSeverity(rule, 'single');
317
+ }
318
+ }
319
+ else {
320
+ rule = 'lonely-http';
321
+ severity = index_1.default.lintConfig.getSeverity(rule);
322
+ }
323
+ if (!severity) {
324
+ continue;
325
+ }
326
+ // LintError
318
327
  const pos = this.posFromIndex(index), { line: startLine, character: startCol } = (0, lint_1.getEndPos)(top, left, pos.top + 1, pos.left), e = {
319
- rule: ruleMap[char],
320
- message: index_1.default.msg('lonely "$1"', magicLink || char === 'h' || error === '-{' || error === '}-' ? error : char),
328
+ rule,
329
+ message: index_1.default.msg('lonely "$1"', magicLink || char === 'h' || lConverter || rConverter ? error : char),
321
330
  severity,
322
331
  startIndex,
323
332
  endIndex,
@@ -326,17 +335,18 @@ let AstText = (() => {
326
335
  startCol,
327
336
  endCol: startCol + length,
328
337
  };
338
+ // Suggestions
329
339
  if (char === '<') {
330
340
  e.suggestions = [{ desc: 'escape', range: [startIndex, startIndex + 1], text: '&lt;' }];
331
341
  }
332
342
  else if (char === 'h' && type !== 'link-text' && wordRegex.test(previousChar || '')) {
333
343
  e.suggestions = [{ desc: 'whitespace', range: [startIndex, startIndex], text: ' ' }];
334
344
  }
335
- else if (char === '[' && type === 'ext-link-text') {
345
+ else if (lbrack && type === 'ext-link-text') {
336
346
  const i = parentNode.getAbsoluteIndex() + parentNode.toString().length;
337
347
  e.suggestions = [{ desc: 'escape', range: [i, i + 1], text: '&#93;' }];
338
348
  }
339
- else if (char === ']' && previousType === 'free-ext-link' && severity === 'error') {
349
+ else if (rbrack && brokenExtLink) {
340
350
  const i = start - previousSibling.toString().length;
341
351
  e.suggestions = [{ desc: 'left bracket', range: [i, i], text: '[' }];
342
352
  }
@@ -36,6 +36,13 @@ export declare class Title {
36
36
  /** @throws `RangeError` undefined namespace */
37
37
  set ns(ns: number);
38
38
  set fragment(fragment: string | undefined);
39
+ /**
40
+ * display title
41
+ *
42
+ * 用于显示的标题
43
+ * @since v1.22.0
44
+ */
45
+ get displayTitle(): string;
39
46
  /**
40
47
  * @see MediaWikiTitleCodec::splitTitleString
41
48
  *
@@ -75,14 +82,14 @@ export declare class Title {
75
82
  * 转换为主页面
76
83
  * @since v1.1.0
77
84
  */
78
- toSubjectPage(): void;
85
+ toSubjectPage(): this;
79
86
  /**
80
87
  * Get the title of its talk page
81
88
  *
82
89
  * 转换为讨论页面
83
90
  * @since v1.1.0
84
91
  */
85
- toTalkPage(): void;
92
+ toTalkPage(): this;
86
93
  /**
87
94
  * Check if the title is a talk page
88
95
  *
@@ -96,12 +103,12 @@ export declare class Title {
96
103
  * 转换为上一级页面
97
104
  * @since v1.1.0
98
105
  */
99
- toBasePage(): void;
106
+ toBasePage(): this;
100
107
  /**
101
108
  * Get the title of its root page
102
109
  *
103
110
  * 转换为根页面
104
111
  * @since v1.1.0
105
112
  */
106
- toRootPage(): void;
113
+ toRootPage(): this;
107
114
  }
package/dist/lib/title.js CHANGED
@@ -94,6 +94,15 @@ class Title {
94
94
  .replaceAll(' ', '_');
95
95
  }
96
96
  }
97
+ /**
98
+ * display title
99
+ *
100
+ * 用于显示的标题
101
+ * @since v1.22.0
102
+ */
103
+ get displayTitle() {
104
+ return this.title.replaceAll('_', ' ');
105
+ }
97
106
  /* NOT FOR BROWSER END */
98
107
  /**
99
108
  * @see MediaWikiTitleCodec::splitTitleString
@@ -236,12 +245,9 @@ class Title {
236
245
  /* NOT FOR BROWSER */
237
246
  /** @private */
238
247
  toString(display) {
239
- return (display ? this.title.replace(/_/gu, ' ') : this.title)
240
- + (this.#fragment === undefined
241
- && this.#redirectFragment === undefined
242
- ? ''
243
- : `#${this.#fragment
244
- ?? this.#redirectFragment}`);
248
+ return (display ? this.displayTitle : this.title) + (this.#fragment === undefined && this.#redirectFragment === undefined
249
+ ? ''
250
+ : `#${this.#fragment ?? this.#redirectFragment}`);
245
251
  }
246
252
  /**
247
253
  * 处理重定向
@@ -283,6 +289,7 @@ class Title {
283
289
  if (this.isTalkPage()) {
284
290
  this.#ns--;
285
291
  }
292
+ return this;
286
293
  }
287
294
  /**
288
295
  * Get the title of its talk page
@@ -294,6 +301,7 @@ class Title {
294
301
  if (!this.isTalkPage()) {
295
302
  this.#ns++;
296
303
  }
304
+ return this;
297
305
  }
298
306
  /**
299
307
  * Check if the title is a talk page
@@ -312,6 +320,7 @@ class Title {
312
320
  */
313
321
  toBasePage() {
314
322
  this.main = this.main.replace(/\/[^/]*$/u, '');
323
+ return this;
315
324
  }
316
325
  /**
317
326
  * Get the title of its root page
@@ -321,6 +330,7 @@ class Title {
321
330
  */
322
331
  toRootPage() {
323
332
  this.main = this.main.replace(/\/.*/u, '');
333
+ return this;
324
334
  }
325
335
  /** @private */
326
336
  getTitleAttr() {
@@ -19,7 +19,9 @@ const readOnly = (readonly = false) =>
19
19
  index_1.default.viewOnly = readonly;
20
20
  }
21
21
  const result = method.apply(this, args);
22
- index_1.default.viewOnly = viewOnly;
22
+ if (!debug_1.Shadow.running) {
23
+ index_1.default.viewOnly = viewOnly;
24
+ }
23
25
  return result;
24
26
  };
25
27
  exports.readOnly = readOnly;
@@ -16,7 +16,9 @@ const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', {
16
16
  const noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly';
17
17
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
18
18
  /<!--[\s\S]*?(?:-->|$)|<foo(?:\s[^>]*)?\/?>|<\/foo\s*>|<(bar)(\s[^>]*?)?(?:\/>|>([\s\S]*?)<\/(\1\s*)>)|<(baz)(\s[^>]*?)?(?:\/>|>([\s\S]*?)(?:<\/(baz\s*)>|$))/giu;
19
- return (0, common_1.getObjRegex)(ext => new RegExp(String.raw `<!--[\s\S]*?(?:-->|$)|<${noincludeRegex}(?:\s[^>]*)?/?>|</${noincludeRegex}\s*>|<(${ext.join('|')})(\s[^>]*?)?(?:/>|>([\s\S]*?)</(\1\s*)>)|<(${includeRegex})(\s[^>]*?)?(?:/>|>([\s\S]*?)(?:</(${includeRegex}\s*)>|$))`, 'giu'));
19
+ return (0, common_1.getObjRegex)(ext => new RegExp(String.raw `<!--[\s\S]*?(?:-->|$)|<${noincludeRegex}(?:\s[^>]*)?/?>|</${noincludeRegex}\s*>|<(${ext.filter(tag => tag !== 'img').join('|')
20
+ // eslint-disable-next-line unicorn/prefer-string-raw
21
+ })(\s[^>]*?)?(?:/>|>([\s\S]*?)</(${'\\1'}\s*)>)|<(${includeRegex})(\s[^>]*?)?(?:/>|>([\s\S]*?)(?:</(${includeRegex}\s*)>|$))${ext.includes('img') ? String.raw `|<img(\s[^>]*?)?(/?)>` : ''}`, 'giu'));
20
22
  });
21
23
  /**
22
24
  * 更新`<onlyinclude>`和`</onlyinclude>`的位置
@@ -77,13 +79,13 @@ const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
77
79
  });
78
80
  wikitext = (0, string_1.restore)(wikitext, stack);
79
81
  }
80
- return wikitext.replace(getRegex[includeOnly ? 1 : 0](newExt), (substr, name, attr, inner, closing, include, includeAttr, includeInner, includeClosing) => {
82
+ return wikitext.replace(getRegex[includeOnly ? 1 : 0](newExt), (substr, name, attr, inner, closing, include, includeAttr, includeInner, includeClosing, imgAttr, imgClosing) => {
81
83
  const l = accum.length;
82
84
  let ch = 'n';
83
- if (name) {
85
+ if (name || newExt.includes('img') && imgClosing !== undefined) {
84
86
  ch = 'e';
85
87
  // @ts-expect-error abstract class
86
- new ext_1.ExtToken(name, attr, inner, closing, newConfig, include, accum);
88
+ new ext_1.ExtToken(name ?? 'img', name ? attr : imgAttr, inner, name ? closing : imgClosing && undefined, newConfig, include, accum);
87
89
  }
88
90
  else if (substr.startsWith('<!--')) {
89
91
  ch = 'c';
@@ -14,7 +14,7 @@ const constants_1 = require("../util/constants");
14
14
  const parseConverter = (text, config, accum) => {
15
15
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
16
16
  /;(?=(?:[^;]*?=>)?\s*zh\s*:|(?:\s|\0\d+[cn]\x7F)*$)/u;
17
- config.regexConverter ??= new RegExp(String.raw `;(?=(?:[^;]*?=>)?\s*(?:${config.variants.join('|')})\s*:|(?:\s|\0\d+[cn]\x7F)*$)`, 'u');
17
+ config.regexConverter ??= new RegExp(String.raw `;(?=(?:[^;]*?=>)?\s*(?:${config.variants.join('|')})\s*:|(?:\s|\0\d+[cn]\x7F)*$)`, 'iu');
18
18
  const regex1 = /-\{/gu, regex2 = /-\{|\}-/gu, stack = [];
19
19
  let regex = regex1, mt = regex.exec(text);
20
20
  while (mt) {
@@ -15,8 +15,8 @@ const attributes_1 = require("../lib/attributes");
15
15
  */
16
16
  const basic = (selector, type, name) => {
17
17
  if (selector.includes('#')) {
18
- const [t, ...names] = selector.split('#');
19
- return (!t || t === type) && names.every(n => n === name);
18
+ const i = selector.indexOf('#');
19
+ return (i === 0 || selector.slice(0, i) === type) && selector.slice(i + 1) === name;
20
20
  }
21
21
  return !selector || selector === type;
22
22
  };
@@ -450,12 +450,14 @@ const checkToken = (selector, scope, has) => (token) => {
450
450
  * @param scope 作用对象
451
451
  * @param has `:has()`伪选择器
452
452
  */
453
- const getCondition = (selector, scope, has) => (
454
- /* eslint-disable @stylistic/operator-linebreak */
455
- /[^a-z\-,#\s]|(?<![\s,])\s+(?![\s,])/u.test(selector.trim()) ?
456
- checkToken(selector, scope, has) :
457
- ({ type, name }) => selector.split(',').some(str => basic(str.trim(), type, name))
458
- /* eslint-enable @stylistic/operator-linebreak */
459
- );
453
+ const getCondition = (selector, scope, has) => {
454
+ /* NOT FOR BROWSER */
455
+ if (/[^a-z\-,#\s]|(?<![\s,])\s+(?![\s,])/u.test(selector.trim())) {
456
+ return checkToken(selector, scope, has);
457
+ }
458
+ /* NOT FOR BROWSER END */
459
+ const parts = selector.split(',');
460
+ return (({ type, name }) => parts.some(str => basic(str.trim(), type, name)));
461
+ };
460
462
  exports.getCondition = getCondition;
461
463
  constants_1.parsers['parseSelector'] = __filename;