wikiparser-node 1.14.1 → 1.15.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 (49) hide show
  1. package/README.md +17 -1
  2. package/bundle/bundle.es7.js +27 -27
  3. package/bundle/bundle.min.js +27 -27
  4. package/config/.schema.json +72 -29
  5. package/config/default.json +331 -241
  6. package/config/enwiki.json +206 -929
  7. package/config/llwiki.json +289 -241
  8. package/config/minimum.json +21 -11
  9. package/config/moegirl.json +290 -251
  10. package/config/zhwiki.json +330 -706
  11. package/coverage/badge.svg +1 -0
  12. package/dist/addon/table.js +1 -1
  13. package/dist/addon/token.js +18 -14
  14. package/dist/addon/transclude.js +5 -1
  15. package/dist/base.d.ts +4 -3
  16. package/dist/index.js +18 -6
  17. package/dist/lib/element.js +4 -0
  18. package/dist/lib/node.js +8 -3
  19. package/dist/lib/range.js +13 -6
  20. package/dist/lib/ranges.js +5 -2
  21. package/dist/lib/text.js +7 -6
  22. package/dist/lib/title.js +1 -0
  23. package/dist/mixin/attributesParent.d.ts +1 -1
  24. package/dist/parser/braces.js +1 -0
  25. package/dist/parser/externalLinks.js +1 -1
  26. package/dist/parser/hrAndDoubleUnderscore.js +1 -1
  27. package/dist/parser/magicLinks.js +1 -1
  28. package/dist/parser/selector.js +10 -2
  29. package/dist/src/atom.js +1 -0
  30. package/dist/src/attribute.d.ts +6 -4
  31. package/dist/src/attributes.js +3 -4
  32. package/dist/src/link/base.js +1 -0
  33. package/dist/src/link/file.js +3 -0
  34. package/dist/src/link/index.js +5 -6
  35. package/dist/src/link/redirectTarget.d.ts +2 -3
  36. package/dist/src/link/redirectTarget.js +1 -2
  37. package/dist/src/nowiki/comment.js +1 -0
  38. package/dist/src/onlyinclude.js +1 -0
  39. package/dist/src/table/index.d.ts +0 -3
  40. package/dist/src/table/index.js +2 -1
  41. package/dist/src/table/trBase.js +3 -2
  42. package/dist/src/transclude.js +5 -10
  43. package/dist/util/debug.js +10 -2
  44. package/dist/util/diff.js +5 -0
  45. package/dist/util/string.js +12 -1
  46. package/extensions/dist/base.js +1 -1
  47. package/extensions/dist/test-page.js +89 -0
  48. package/extensions/es7/base.js +1 -1
  49. package/package.json +12 -6
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="98" height="20" role="img" aria-label="Coverage: 85%"><title>Coverage: 85%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="98" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="35" height="20" fill="#4c1"/><rect width="98" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="795" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="250">85%</text><text x="795" y="140" transform="scale(.1)" fill="#fff" textLength="250">85%</text></g></svg>
@@ -280,7 +280,7 @@ index_2.TableToken.prototype.removeTableCol =
280
280
  index_2.TableToken.prototype.mergeCells =
281
281
  /** @implements */
282
282
  function (xlim, ylim) {
283
- const layout = this.getLayout(), maxCol = Math.max(...layout.map(({ length }) => length)), [xmin, xmax] = xlim.map(x => x < 0 ? x + maxCol : x).sort(), [ymin, ymax] = ylim.map(y => y < 0 ? y + layout.length : y).sort(), set = new Set(layout.slice(ymin, ymax).flatMap(rowLayout => rowLayout.slice(xmin, xmax)));
283
+ const layout = this.getLayout(), maxCol = Math.max(...layout.map(({ length }) => length)), [xmin, xmax] = xlim.map(x => x < 0 ? x + maxCol : x).sort(debug_1.compare), [ymin, ymax] = ylim.map(y => y < 0 ? y + layout.length : y).sort(debug_1.compare), set = new Set(layout.slice(ymin, ymax).flatMap(rowLayout => rowLayout.slice(xmin, xmax)));
284
284
  if ([...layout[ymin - 1] ?? [], ...layout[ymax] ?? []].some(coords => set.has(coords))
285
285
  || layout.some(rowLayout => set.has(rowLayout[xmin - 1]) || set.has(rowLayout[xmax]))) {
286
286
  throw new RangeError('The area to be merged overlaps with the outer area!');
@@ -37,6 +37,7 @@ index_2.Token.prototype.createElement = /** @implements */ function (tagName, {
37
37
  return new html_1.HtmlToken(tagName, attr, Boolean(closing), Boolean(selfClosing), config);
38
38
  });
39
39
  }
40
+ /* istanbul ignore next */
40
41
  throw new RangeError(`Invalid tag name: ${tagName}`);
41
42
  };
42
43
  index_2.Token.prototype.caretPositionFromIndex = /** @implements */ function (index) {
@@ -103,6 +104,7 @@ index_2.Token.prototype.sections = /** @implements */ function () {
103
104
  index_2.Token.prototype.findEnclosingHtml = /** @implements */ function (tag) {
104
105
  tag &&= tag.toLowerCase();
105
106
  const { html } = this.getAttribute('config'), normalTags = new Set(html[0]), voidTags = new Set(html[2]);
107
+ /* istanbul ignore next */
106
108
  if (html[2].includes(tag)) {
107
109
  throw new RangeError(`Void tag: ${tag}`);
108
110
  }
@@ -159,6 +161,20 @@ const implicitNewLine = (str, prev) => prev + (prev !== '\n' && /^(?:\{\||[:;#*]
159
161
  * @param b
160
162
  */
161
163
  const cmp = (a, b) => a === b || Boolean(a && b) && Number(a) === Number(b);
164
+ /**
165
+ * 解析 if/ifexist/ifeq 解析器函数
166
+ * @param accum
167
+ * @param prev 解析器函数前的字符串
168
+ * @param effective 生效的参数
169
+ */
170
+ const parseIf = (accum, prev, effective) => {
171
+ if (effective) {
172
+ // @ts-expect-error sparse array
173
+ accum[accum.indexOf(effective.lastChild)] = undefined;
174
+ return implicitNewLine(effective.value, prev);
175
+ }
176
+ return prev;
177
+ };
162
178
  /**
163
179
  * 展开模板
164
180
  * @param wikitext
@@ -246,22 +262,10 @@ const expand = (wikitext, config, include, context, accum = [], stack = []) => {
246
262
  const title = index_1.default.normalizeTitle(var1, 0, include, config, true);
247
263
  bool = title.valid && !title.interwiki;
248
264
  }
249
- const effective = c[bool ? 2 : 3];
250
- if (effective) {
251
- // @ts-expect-error sparse array
252
- accum[accum.indexOf(effective.lastChild)] = undefined;
253
- return implicitNewLine(effective.value, prev);
254
- }
255
- return prev;
265
+ return parseIf(accum, prev, c[bool ? 2 : 3]);
256
266
  }
257
267
  else if (known && name === 'ifeq' && !/\0\d+t\x7F/u.test(var2)) {
258
- const effective = c[cmp(var1, var2) ? 3 : 4];
259
- if (effective) {
260
- // @ts-expect-error sparse array
261
- accum[accum.indexOf(effective.lastChild)] = undefined;
262
- return implicitNewLine(effective.value, prev);
263
- }
264
- return prev;
268
+ return parseIf(accum, prev, c[cmp(var1, var2) ? 3 : 4]);
265
269
  }
266
270
  else if (known && name === 'switch') {
267
271
  let defaultVal = '', found = false, transclusion = false, defaultParam;
@@ -22,6 +22,7 @@ transclude_1.TranscludeToken.prototype.newAnonArg =
22
22
  transclude_1.TranscludeToken.prototype.setValue =
23
23
  /** @implements */
24
24
  function (key, value) {
25
+ /* istanbul ignore if */
25
26
  if (!this.isTemplate()) {
26
27
  throw new Error('TranscludeToken.setValue method is only for templates!');
27
28
  }
@@ -41,6 +42,7 @@ transclude_1.TranscludeToken.prototype.setValue =
41
42
  transclude_1.TranscludeToken.prototype.replaceTemplate =
42
43
  /** @implements */
43
44
  function (title) {
45
+ /* istanbul ignore if */
44
46
  if (this.type === 'magic-word') {
45
47
  throw new Error('TranscludeToken.replaceTemplate method is only for templates!');
46
48
  }
@@ -50,6 +52,7 @@ transclude_1.TranscludeToken.prototype.replaceTemplate =
50
52
  transclude_1.TranscludeToken.prototype.replaceModule =
51
53
  /** @implements */
52
54
  function (title) {
55
+ /* istanbul ignore if */
53
56
  if (this.type !== 'magic-word' || this.name !== 'invoke') {
54
57
  throw new Error('TranscludeToken.replaceModule method is only for modules!');
55
58
  }
@@ -66,6 +69,7 @@ transclude_1.TranscludeToken.prototype.replaceModule =
66
69
  transclude_1.TranscludeToken.prototype.replaceFunction =
67
70
  /** @implements */
68
71
  function (func) {
72
+ /* istanbul ignore next */
69
73
  if (this.type !== 'magic-word' || this.name !== 'invoke') {
70
74
  throw new Error('TranscludeToken.replaceModule method is only for modules!');
71
75
  }
@@ -157,7 +161,6 @@ transclude_1.TranscludeToken.prototype.fixDuplication =
157
161
  return `Line ${String(top)} Column ${String(left)}`;
158
162
  }).join('\n')}`);
159
163
  duplicatedKeys.push(key);
160
- continue;
161
164
  }
162
165
  }
163
166
  return duplicatedKeys;
@@ -175,6 +178,7 @@ transclude_1.TranscludeToken.prototype.escapeTables =
175
178
  }
176
179
  }
177
180
  const { firstChild, length } = index_1.default.parse(`{{${parsed.toString()}}}`, include, undefined, config);
181
+ /* istanbul ignore if */
178
182
  if (length !== 1 || !(firstChild instanceof transclude_1.TranscludeToken)) {
179
183
  throw new Error('Failed to escape tables!');
180
184
  }
package/dist/base.d.ts CHANGED
@@ -3,13 +3,14 @@ export interface Config {
3
3
  readonly html: [string[], string[], string[]];
4
4
  readonly namespaces: Record<string, string>;
5
5
  readonly nsid: Record<string, number>;
6
- readonly parserFunction: [Record<string, string>, string[], string[], string[]];
7
- readonly doubleUnderscore: [string[], string[], Record<string, string>?];
6
+ readonly variable: string[];
7
+ readonly parserFunction: [Record<string, string>, Record<string, string> | string[], string[], string[]];
8
+ readonly doubleUnderscore: [string[], string[], Record<string, string>?, Record<string, string>?];
8
9
  readonly protocol: string;
10
+ readonly interwiki: string[];
9
11
  readonly img: Record<string, string>;
10
12
  readonly redirection: string[];
11
13
  readonly variants: string[];
12
- readonly interwiki: string[];
13
14
  readonly conversionTable?: [string, string][];
14
15
  readonly redirects?: [string, string][];
15
16
  readonly articlePath?: string;
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ const chalk = require("chalk");
14
14
  * @param file 文件名
15
15
  * @param dir 子路径
16
16
  */
17
- const rootRequire = (file, dir) => require(path.isAbsolute(file) ? file : path.join('..', file.includes('/') ? '' : dir, file));
17
+ const rootRequire = (file, dir) => require(path.isAbsolute(file) ? /* istanbul ignore next */ file : path.join('..', file.includes('/') ? '' : dir, file));
18
18
  /* NOT FOR BROWSER */
19
19
  /**
20
20
  * 快速规范化页面标题
@@ -70,14 +70,17 @@ const Parser = {
70
70
  getConfig() {
71
71
  if (typeof this.config === 'string') {
72
72
  this.config = rootRequire(this.config, 'config');
73
- if (this.config.doubleUnderscore.length < 3) {
73
+ /* istanbul ignore if */
74
+ if (this.config.doubleUnderscore.length < 3 || Array.isArray(this.config.parserFunction[1])) {
74
75
  (0, diff_1.error)(`The schema (${path.resolve(__dirname, '..', 'config', '.schema.json')}) of parser configuration is updated.`);
75
76
  }
76
77
  /* NOT FOR BROWSER */
77
78
  const { config: { conversionTable, redirects } } = this;
79
+ /* istanbul ignore if */
78
80
  if (conversionTable) {
79
81
  this.conversionTable = new Map(conversionTable);
80
82
  }
83
+ /* istanbul ignore if */
81
84
  if (redirects) {
82
85
  this.redirects = new Map(redirects);
83
86
  }
@@ -85,8 +88,10 @@ const Parser = {
85
88
  return this.getConfig();
86
89
  }
87
90
  const { doubleUnderscore } = this.config;
88
- if (doubleUnderscore.length > 2 && doubleUnderscore[0].length === 0) {
89
- doubleUnderscore[0] = Object.keys(doubleUnderscore[2]);
91
+ for (let i = 0; i < 2; i++) {
92
+ if (doubleUnderscore.length > i + 2 && doubleUnderscore[i].length === 0) {
93
+ doubleUnderscore[i] = Object.keys(doubleUnderscore[i + 2]);
94
+ }
90
95
  }
91
96
  return {
92
97
  ...this.config,
@@ -150,11 +155,11 @@ const Parser = {
150
155
  try {
151
156
  return token.parse(maxStage, include);
152
157
  }
153
- catch (e) {
158
+ catch (e) /* istanbul ignore next */ {
154
159
  if (e instanceof Error) {
155
160
  const file = path.join(__dirname, '..', 'errors', new Date().toISOString()), stage = token.getAttribute('stage');
156
161
  for (const k in config) {
157
- if (k.startsWith('regex')) {
162
+ if (k.startsWith('regex') || config[k] instanceof Set) {
158
163
  delete config[k];
159
164
  }
160
165
  }
@@ -166,6 +171,7 @@ const Parser = {
166
171
  }
167
172
  });
168
173
  /* NOT FOR BROWSER */
174
+ /* istanbul ignore if */
169
175
  if (this.debugging) {
170
176
  let restored = root.toString(), process = 'parsing';
171
177
  if (restored === wikitext) {
@@ -188,29 +194,34 @@ const Parser = {
188
194
  /* NOT FOR BROWSER */
189
195
  /** @implements */
190
196
  warn(msg, ...args) {
197
+ /* istanbul ignore if */
191
198
  if (this.warning) {
192
199
  console.warn(chalk.yellow(msg), ...args);
193
200
  }
194
201
  },
195
202
  /** @implements */
196
203
  debug(msg, ...args) {
204
+ /* istanbul ignore if */
197
205
  if (this.debugging) {
198
206
  console.debug(chalk.blue(msg), ...args);
199
207
  }
200
208
  },
201
209
  error: diff_1.error,
202
210
  info: diff_1.info,
211
+ /* istanbul ignore next */
203
212
  /** @implements */
204
213
  log(f) {
205
214
  if (typeof f === 'function') {
206
215
  console.log(String(f));
207
216
  }
208
217
  },
218
+ /* istanbul ignore next */
209
219
  /** @implements */
210
220
  require(name) {
211
221
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
212
222
  return Object.hasOwn(constants_1.classes, name) ? require(constants_1.classes[name])[name] : require(path.join(__dirname, name));
213
223
  },
224
+ /* istanbul ignore next */
214
225
  /** @implements */
215
226
  async clearCache() {
216
227
  await (0, diff_1.cmd)('npm', ['--prefix', path.join(__dirname, '..'), 'run', 'build:core']);
@@ -238,6 +249,7 @@ const Parser = {
238
249
  .exec(title.replaceAll('_', ' ').replace(/^\s*:?\s*/u, ''))
239
250
  : null;
240
251
  },
252
+ /* istanbul ignore next */
241
253
  /** @implements */
242
254
  reparse(date = '') {
243
255
  const main = fs.readdirSync(path.join(__dirname, '..', 'errors'))
@@ -124,6 +124,7 @@ class AstElement extends node_1.AstNode {
124
124
  */
125
125
  insertAt(node, i = this.length) {
126
126
  /* NOT FOR BROWSER */
127
+ /* istanbul ignore next */
127
128
  if (node.contains(this)) {
128
129
  throw new RangeError('Cannot insert an ancestor node!');
129
130
  }
@@ -240,6 +241,7 @@ class AstElement extends node_1.AstNode {
240
241
  return data;
241
242
  }
242
243
  /* NOT FOR BROWSER */
244
+ /* istanbul ignore next */
243
245
  throw new RangeError(`The child node at position ${i} is ${oldText.constructor.name}!`);
244
246
  }
245
247
  /** @private */
@@ -285,6 +287,7 @@ class AstElement extends node_1.AstNode {
285
287
  cur += length + this.getGaps(i);
286
288
  }
287
289
  /* NOT FOR BROWSER */
290
+ /* istanbul ignore if */
288
291
  if (typeof file === 'string') {
289
292
  fs.writeFileSync(path.join(__dirname, '..', '..', 'printed', file + (file.endsWith('.json') ? '' : '.json')), JSON.stringify(json, null, 2));
290
293
  }
@@ -344,6 +347,7 @@ class AstElement extends node_1.AstNode {
344
347
  */
345
348
  #getChildIndex(node) {
346
349
  const i = this.childNodes.indexOf(node);
350
+ /* istanbul ignore if */
347
351
  if (i === -1) {
348
352
  throw new RangeError('Not a child node!');
349
353
  }
package/dist/lib/node.js CHANGED
@@ -21,6 +21,7 @@ const getDimension = (str) => {
21
21
  const getIndex = (j, parent) => parent.childNodes.slice(0, j).reduce((acc, cur, i) => acc + cur.toString().length + parent.getGaps(i), 0)
22
22
  + parent.getAttribute('padding');
23
23
  /* NOT FOR BROWSER */
24
+ /* istanbul ignore next */
24
25
  /**
25
26
  * 定制TypeError消息
26
27
  * @param {Function} Constructor 类
@@ -54,12 +55,12 @@ class AstNode {
54
55
  /** 后一个兄弟节点 */
55
56
  get nextSibling() {
56
57
  const childNodes = this.parentNode?.childNodes;
57
- return childNodes && childNodes[childNodes.indexOf(this) + 1];
58
+ return childNodes?.[childNodes.indexOf(this) + 1];
58
59
  }
59
60
  /** 前一个兄弟节点 */
60
61
  get previousSibling() {
61
62
  const childNodes = this.parentNode?.childNodes;
62
- return childNodes && childNodes[childNodes.indexOf(this) - 1];
63
+ return childNodes?.[childNodes.indexOf(this) - 1];
63
64
  }
64
65
  /** 行数 */
65
66
  get offsetHeight() {
@@ -271,6 +272,7 @@ class AstNode {
271
272
  return this.type === type;
272
273
  }
273
274
  /* NOT FOR BROWSER */
275
+ /* istanbul ignore next */
274
276
  /** @private */
275
277
  typeError(method, ...types) {
276
278
  return typeError(this.constructor, method, ...types);
@@ -292,6 +294,7 @@ class AstNode {
292
294
  if (e instanceof assert.AssertionError) {
293
295
  return false;
294
296
  }
297
+ /* istanbul ignore next */
295
298
  throw e;
296
299
  }
297
300
  return true;
@@ -304,6 +307,7 @@ class AstNode {
304
307
  */
305
308
  #insertAdjacent(nodes, offset) {
306
309
  const { parentNode } = this;
310
+ /* istanbul ignore if */
307
311
  if (!parentNode) {
308
312
  throw new Error('There is no parent node!');
309
313
  }
@@ -348,6 +352,7 @@ class AstNode {
348
352
  /** @private */
349
353
  verifyChild(i, addition = 0) {
350
354
  const { childNodes: { length } } = this;
355
+ /* istanbul ignore if */
351
356
  if (i < -length || i >= length + addition) {
352
357
  throw new RangeError(`The child node at position ${i} does not exist!`);
353
358
  }
@@ -448,7 +453,7 @@ class AstNode {
448
453
  else if (other.contains(this)) {
449
454
  return 1;
450
455
  }
451
- else if (this.getRootNode() !== other.getRootNode()) {
456
+ else /* istanbul ignore if */ if (this.getRootNode() !== other.getRootNode()) {
452
457
  throw new RangeError('Nodes to be compared are not in the same document!');
453
458
  }
454
459
  const aAncestors = [...this.getAncestors().reverse(), this], bAncestors = [...other.getAncestors().reverse(), other], depth = aAncestors.findIndex((ancestor, i) => bAncestors[i] !== ancestor), { childNodes } = aAncestors[depth - 1];
package/dist/lib/range.js CHANGED
@@ -18,8 +18,10 @@ const getParent = (node) => {
18
18
  if (parentNode) {
19
19
  return parentNode;
20
20
  }
21
+ /* istanbul ignore next */
21
22
  throw new RangeError('The reference node has no parent node!');
22
23
  };
24
+ /* istanbul ignore next */
23
25
  /**
24
26
  * 未初始化时抛出错误
25
27
  * @param start 是否未初始化起点
@@ -36,11 +38,11 @@ class AstRange {
36
38
  #endOffset;
37
39
  /** 起点容器 */
38
40
  get startContainer() {
39
- return this.#startContainer ?? notInit(true);
41
+ return this.#startContainer ?? /* istanbul ignore next */ notInit(true);
40
42
  }
41
43
  /** 起点位置 */
42
44
  get startOffset() {
43
- return this.#startOffset ?? notInit(true);
45
+ return this.#startOffset ?? /* istanbul ignore next */ notInit(true);
44
46
  }
45
47
  /** 起点绝对位置 */
46
48
  get startIndex() {
@@ -52,11 +54,11 @@ class AstRange {
52
54
  }
53
55
  /** 终点容器 */
54
56
  get endContainer() {
55
- return this.#endContainer ?? notInit(false);
57
+ return this.#endContainer ?? /* istanbul ignore next */ notInit(false);
56
58
  }
57
59
  /** 终点位置 */
58
60
  get endOffset() {
59
- return this.#endOffset ?? notInit(false);
61
+ return this.#endOffset ?? /* istanbul ignore next */ notInit(false);
60
62
  }
61
63
  /** 终点绝对位置 */
62
64
  get endIndex() {
@@ -83,12 +85,14 @@ class AstRange {
83
85
  #check() {
84
86
  const { startContainer, startOffset, endContainer, endOffset } = this, msg1 = 'The start and end positions are not siblings!', msg2 = 'The start position cannot be after the end position!';
85
87
  if (startContainer === endContainer) {
88
+ /* istanbul ignore if */
86
89
  if (startOffset > endOffset) {
87
90
  throw new RangeError(msg2);
88
91
  }
89
92
  return;
90
93
  }
91
94
  const { type: startType, parentNode: startParent } = startContainer, { type: endType, parentNode: endParent } = endContainer;
95
+ /* istanbul ignore next */
92
96
  if (startType !== 'text') {
93
97
  if (endType !== 'text' || startContainer !== endParent) {
94
98
  throw new RangeError(msg1);
@@ -121,6 +125,7 @@ class AstRange {
121
125
  */
122
126
  setStart(startNode, offset) {
123
127
  const { length } = startNode;
128
+ /* istanbul ignore if */
124
129
  if (offset < 0 || offset > length) {
125
130
  throw new RangeError(`The range of startOffset should be 0 ~ ${length}`);
126
131
  }
@@ -131,7 +136,7 @@ class AstRange {
131
136
  try {
132
137
  this.#check();
133
138
  }
134
- catch (e) {
139
+ catch (e) /* istanbul ignore next */ {
135
140
  this.#startContainer = startContainer;
136
141
  this.#startOffset = startOffset;
137
142
  throw e;
@@ -146,6 +151,7 @@ class AstRange {
146
151
  */
147
152
  setEnd(endNode, offset) {
148
153
  const { length } = endNode;
154
+ /* istanbul ignore if */
149
155
  if (offset < 0 || offset > length) {
150
156
  throw new RangeError(`The range of endOffset should be 0 ~ ${length}`);
151
157
  }
@@ -156,7 +162,7 @@ class AstRange {
156
162
  try {
157
163
  this.#check();
158
164
  }
159
- catch (e) {
165
+ catch (e) /* istanbul ignore next */ {
160
166
  this.#endContainer = endContainer;
161
167
  this.#endOffset = endOffset;
162
168
  throw e;
@@ -252,6 +258,7 @@ class AstRange {
252
258
  */
253
259
  comparePoint(referenceNode, offset) {
254
260
  const { startContainer, startIndex, endContainer, endIndex } = this;
261
+ /* istanbul ignore if */
255
262
  if (startContainer.getRootNode() !== referenceNode.getRootNode()) {
256
263
  throw new RangeError('The point to be compared is not in the same document!');
257
264
  }
@@ -28,6 +28,7 @@ class Range {
28
28
  this.start = Number(start);
29
29
  this.end = Number(end?.trim() || Infinity);
30
30
  this.step = Math.max(Number(step), 1);
31
+ /* istanbul ignore next */
31
32
  if (!Number.isInteger(this.start)) {
32
33
  throw new RangeError(`The start of a range, \`${start}\`, should be an integer!`);
33
34
  }
@@ -41,9 +42,11 @@ class Range {
41
42
  else {
42
43
  const mt = /^([+-])?(\d+)?n(?:\s*([+-])\s*(\d+))?$/u
43
44
  .exec(str);
45
+ /* istanbul ignore else */
44
46
  if (mt) {
45
47
  const [, sgnA = '+', a = 1, sgnB = '+'] = mt, b = Number(mt[4] ?? 0);
46
48
  this.step = Number(a);
49
+ /* istanbul ignore if */
47
50
  if (this.step === 0) {
48
51
  throw new RangeError(`In the argument \`${str}\`, the coefficient of "n" must not be 0!`);
49
52
  }
@@ -98,7 +101,7 @@ class Ranges extends Array {
98
101
  try {
99
102
  this.push(new Range(ele));
100
103
  }
101
- catch (e) {
104
+ catch (e) /* istanbul ignore next */ {
102
105
  if (e instanceof RangeError) {
103
106
  (0, diff_1.error)(e.message);
104
107
  }
@@ -119,7 +122,7 @@ class Ranges extends Array {
119
122
  }
120
123
  return ele.applyTo(a);
121
124
  })),
122
- ].filter(i => i >= 0 && i < length).sort();
125
+ ].filter(i => i >= 0 && i < length).sort(debug_1.compare);
123
126
  }
124
127
  }
125
128
  exports.Ranges = Ranges;
package/dist/lib/text.js CHANGED
@@ -13,7 +13,7 @@ const html_1 = require("../util/html");
13
13
  /<\s*(?:\/\s*)?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*?\])|((?:^|\])[^[]*?)\]+|https?[:/]\/+/giu;
14
14
  /^https?:\/\/(?:\[[\da-f:.]+\]|[^[\]<>"\t\n\p{Zs}])[^[\]<>"\t\n\p{Zs}]*\.(?:gif|png|jpg|jpeg)$/iu;
15
15
  /* eslint-enable @typescript-eslint/no-unused-expressions, es-x/no-regexp-unicode-property-escapes */
16
- const sp = String.raw `[${string_1.zs}\t]*`, source = String.raw `<\s*(?:/\s*)?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*?\])|((?:^|\])[^[]*?)\]+|(?:rfc|pmid)(?=[-::]?${sp}\d)|isbn(?=[-::]?${sp}(?:\d(?:${sp}|-)){6})`, errorSyntax = new RegExp(String.raw `${source}|https?[:/]/+`, 'giu'), errorSyntaxUrl = new RegExp(source, 'giu'), extImage = new RegExp(String.raw `^https?://${string_1.extUrlCharFirst}${string_1.extUrlChar}\.(?:gif|png|jpg|jpeg)$`, 'iu'), noLinkTypes = new Set(['attr-value', 'ext-link-text', 'link-text']), regexes = {
16
+ const sp = String.raw `[${string_1.zs}\t]*`, source = String.raw `<\s*(?:/\s*)?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*?\])|((?:^|\])[^[]*?)\]+|(?:rfc|pmid)(?=[-::]?${sp}\d)|isbn(?=[-::]?${sp}(?:\d(?:${sp}|-)){6})`, errorSyntax = new RegExp(String.raw `${source}|https?[:/]/+`, 'giu'), errorSyntaxUrl = new RegExp(source, 'giu'), noLinkTypes = new Set(['attr-value', 'ext-link-text', 'link-text']), regexes = {
17
17
  '[': /[[\]]/u,
18
18
  '{': /[{}]/u,
19
19
  ']': /[[\]](?=[^[\]]*$)/u,
@@ -85,7 +85,7 @@ try {
85
85
  // eslint-disable-next-line prefer-regex-literals, es-x/no-regexp-unicode-property-escapes
86
86
  wordRegex = new RegExp(String.raw `[\p{L}\d_]`, 'u');
87
87
  }
88
- catch {
88
+ catch /* istanbul ignore next */ {
89
89
  wordRegex = /\w/u;
90
90
  }
91
91
  /** 文本节点 */
@@ -131,6 +131,7 @@ class AstText extends node_1.AstNode {
131
131
  return [];
132
132
  }
133
133
  const { data, parentNode, nextSibling, previousSibling } = this;
134
+ /* istanbul ignore if */
134
135
  if (!parentNode) {
135
136
  throw new Error('An isolated text node cannot be linted!');
136
137
  }
@@ -178,7 +179,6 @@ class AstText extends node_1.AstNode {
178
179
  if (char === '<' && !tags.has(tag.toLowerCase())
179
180
  || char === '[' && type === 'ext-link-text' && (/&(?:rbrack|#93|#x5[Dd];);/u.test(data.slice(index + 1))
180
181
  || nextSibling?.is('ext') && nextName === 'nowiki' && nextSibling.innerText?.includes(']'))
181
- || char === 'h' && index === 0 && type === 'ext-link-text' && extImage.test(data)
182
182
  || magicLink && (!parentNode.getAttribute('plain') || noLinkTypes.has(type))) {
183
183
  continue;
184
184
  }
@@ -232,9 +232,7 @@ class AstText extends node_1.AstNode {
232
232
  if (char === '<') {
233
233
  e.suggestions = [{ desc: 'escape', range: [startIndex, startIndex + 1], text: '&lt;' }];
234
234
  }
235
- else if (char === 'h'
236
- && !(type === 'ext-link-text' || type === 'link-text')
237
- && wordRegex.test(previousChar || '')) {
235
+ else if (char === 'h' && type !== 'link-text' && wordRegex.test(previousChar || '')) {
238
236
  e.suggestions = [{ desc: 'whitespace', range: [startIndex, startIndex], text: ' ' }];
239
237
  }
240
238
  else if (char === '[' && type === 'ext-link-text') {
@@ -327,10 +325,12 @@ class AstText extends node_1.AstNode {
327
325
  * @throws `Error` 没有父节点
328
326
  */
329
327
  splitText(offset) {
328
+ /* istanbul ignore if */
330
329
  if (offset > this.length || offset < -this.length) {
331
330
  throw new RangeError(`Wrong offset to split: ${offset}`);
332
331
  }
333
332
  const { parentNode, data } = this;
333
+ /* istanbul ignore if */
334
334
  if (!parentNode) {
335
335
  throw new Error('The text node to be split has no parent node!');
336
336
  }
@@ -341,6 +341,7 @@ class AstText extends node_1.AstNode {
341
341
  }
342
342
  /** @private */
343
343
  getRelativeIndex(j) {
344
+ /* istanbul ignore else */
344
345
  if (j === undefined) {
345
346
  return super.getRelativeIndex();
346
347
  }
package/dist/lib/title.js CHANGED
@@ -62,6 +62,7 @@ class Title {
62
62
  }
63
63
  /** @throws `RangeError` undefined namespace */
64
64
  set ns(ns) {
65
+ /* istanbul ignore if */
65
66
  if (!(this.ns in this.#namespaces)) {
66
67
  throw new RangeError('Undefined namespace!');
67
68
  }
@@ -4,7 +4,7 @@ export interface AttributesParentBase {
4
4
  /** 以字符串表示的class属性 */
5
5
  className: string;
6
6
  /** 以Set表示的class属性 */
7
- classList: Set<string>;
7
+ readonly classList: Set<string>;
8
8
  /** id属性 */
9
9
  id: string;
10
10
  /**
@@ -105,6 +105,7 @@ const parseBraces = (wikitext, config, accum) => {
105
105
  }
106
106
  }
107
107
  catch (e) {
108
+ /* istanbul ignore else */
108
109
  if (e instanceof SyntaxError && e.message === 'Invalid template name') {
109
110
  skip = true;
110
111
  }
@@ -21,9 +21,9 @@ const parseExternalLinks = (wikitext, config, accum, inFile) => {
21
21
  return wikitext.replace(config.regexExternalLinks, (_, url, space, text) => {
22
22
  const { length } = accum, mt = /&[lg]t;/u.exec(url);
23
23
  if (mt) {
24
- url = url.slice(0, mt.index);
25
24
  space = '';
26
25
  text = url.slice(mt.index) + space + text;
26
+ url = url.slice(0, mt.index);
27
27
  }
28
28
  if (inFile) {
29
29
  // @ts-expect-error abstract class
@@ -32,7 +32,7 @@ const parseHrAndDoubleUnderscore = ({ firstChild: { data }, type, name }, config
32
32
  if (caseSensitive || caseInsensitive) {
33
33
  // @ts-expect-error abstract class
34
34
  new doubleUnderscore_1.DoubleUnderscoreToken(p1, caseSensitive, config, accum);
35
- return `\0${accum.length - 1}${caseInsensitive && (aliases?.[lc] ?? lc) === 'toc' ? 'u' : 'n'}\x7F`;
35
+ return `\0${accum.length - 1}${caseInsensitive && (aliases?.[lc] ?? /* istanbul ignore next */ lc) === 'toc' ? 'u' : 'n'}\x7F`;
36
36
  }
37
37
  return m;
38
38
  }).replace(/^((?:\0\d+[cn]\x7F)*)(={1,6})(.+)\2((?:\s|\0\d+[cn]\x7F)*)$/gmu, (_, lead, equals, heading, trail) => {
@@ -20,7 +20,7 @@ const parseMagicLinks = (wikitext, config, accum) => {
20
20
  try {
21
21
  config.regexMagicLinks = new RegExp(String.raw `(^|[^\p{L}\d_])(?:(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})|${magicLinkPattern})`, 'giu');
22
22
  }
23
- catch {
23
+ catch /* istanbul ignore next */ {
24
24
  config.regexMagicLinks = new RegExp(String.raw `(^|\W)(?:(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})|${magicLinkPattern})`, 'giu');
25
25
  }
26
26
  }