wikiparser-node 1.9.0 → 1.9.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 (111) hide show
  1. package/README.en.md +2 -2
  2. package/dist/addon/table.js +9 -8
  3. package/dist/addon/token.js +8 -5
  4. package/dist/addon/transclude.js +12 -10
  5. package/dist/base.d.ts +2 -0
  6. package/dist/index.d.ts +2 -4
  7. package/dist/index.js +5 -2
  8. package/dist/lib/element.d.ts +0 -5
  9. package/dist/lib/element.js +46 -39
  10. package/dist/lib/node.d.ts +1 -1
  11. package/dist/lib/node.js +9 -9
  12. package/dist/lib/rect.d.ts +18 -0
  13. package/dist/lib/rect.js +34 -0
  14. package/dist/lib/text.js +8 -13
  15. package/dist/mixin/attributesParent.js +2 -1
  16. package/dist/mixin/fixed.js +5 -3
  17. package/dist/mixin/flagsParent.js +2 -1
  18. package/dist/mixin/hidden.d.ts +2 -0
  19. package/dist/mixin/hidden.js +9 -4
  20. package/dist/mixin/magicLinkParent.js +2 -1
  21. package/dist/mixin/singleLine.js +3 -2
  22. package/dist/mixin/sol.js +3 -2
  23. package/dist/mixin/syntax.js +3 -6
  24. package/dist/parser/braces.js +13 -6
  25. package/dist/parser/commentAndExt.js +3 -13
  26. package/dist/parser/converter.js +4 -5
  27. package/dist/parser/externalLinks.js +2 -9
  28. package/dist/parser/hrAndDoubleUnderscore.js +3 -1
  29. package/dist/parser/html.js +1 -1
  30. package/dist/parser/links.js +3 -1
  31. package/dist/parser/magicLinks.js +7 -13
  32. package/dist/parser/redirect.js +1 -1
  33. package/dist/parser/selector.js +8 -8
  34. package/dist/src/arg.d.ts +0 -2
  35. package/dist/src/arg.js +6 -7
  36. package/dist/src/atom.d.ts +2 -2
  37. package/dist/src/atom.js +4 -5
  38. package/dist/src/attribute.d.ts +0 -2
  39. package/dist/src/attribute.js +7 -19
  40. package/dist/src/attributes.d.ts +0 -2
  41. package/dist/src/attributes.js +11 -17
  42. package/dist/src/converter.d.ts +0 -2
  43. package/dist/src/converter.js +1 -1
  44. package/dist/src/converterFlags.d.ts +0 -2
  45. package/dist/src/converterFlags.js +3 -2
  46. package/dist/src/converterRule.d.ts +0 -2
  47. package/dist/src/converterRule.js +10 -9
  48. package/dist/src/extLink.d.ts +0 -2
  49. package/dist/src/extLink.js +1 -1
  50. package/dist/src/gallery.d.ts +0 -2
  51. package/dist/src/gallery.js +3 -3
  52. package/dist/src/heading.d.ts +0 -2
  53. package/dist/src/heading.js +4 -9
  54. package/dist/src/hidden.d.ts +4 -1
  55. package/dist/src/hidden.js +69 -13
  56. package/dist/src/html.d.ts +2 -5
  57. package/dist/src/html.js +19 -38
  58. package/dist/src/imageParameter.d.ts +0 -2
  59. package/dist/src/imageParameter.js +11 -7
  60. package/dist/src/imagemap.d.ts +0 -2
  61. package/dist/src/imagemap.js +7 -6
  62. package/dist/src/index.d.ts +1 -13
  63. package/dist/src/index.js +58 -58
  64. package/dist/src/link/base.d.ts +0 -2
  65. package/dist/src/link/base.js +5 -9
  66. package/dist/src/link/file.js +11 -9
  67. package/dist/src/link/galleryImage.js +2 -2
  68. package/dist/src/link/index.d.ts +0 -2
  69. package/dist/src/link/index.js +5 -5
  70. package/dist/src/link/redirectTarget.d.ts +0 -12
  71. package/dist/src/link/redirectTarget.js +4 -10
  72. package/dist/src/magicLink.d.ts +1 -0
  73. package/dist/src/magicLink.js +29 -19
  74. package/dist/src/nested.js +3 -3
  75. package/dist/src/nowiki/base.d.ts +2 -2
  76. package/dist/src/nowiki/base.js +3 -4
  77. package/dist/src/nowiki/comment.d.ts +3 -9
  78. package/dist/src/nowiki/comment.js +106 -59
  79. package/dist/src/nowiki/doubleUnderscore.d.ts +3 -3
  80. package/dist/src/nowiki/doubleUnderscore.js +3 -4
  81. package/dist/src/nowiki/index.js +3 -1
  82. package/dist/src/nowiki/noinclude.d.ts +1 -1
  83. package/dist/src/nowiki/noinclude.js +63 -13
  84. package/dist/src/nowiki/quote.js +18 -24
  85. package/dist/src/onlyinclude.js +1 -1
  86. package/dist/src/paramTag/index.d.ts +0 -2
  87. package/dist/src/paramTag/index.js +3 -3
  88. package/dist/src/parameter.js +10 -9
  89. package/dist/src/redirect.d.ts +4 -1
  90. package/dist/src/redirect.js +5 -3
  91. package/dist/src/syntax.d.ts +2 -2
  92. package/dist/src/syntax.js +4 -5
  93. package/dist/src/table/index.d.ts +2 -3
  94. package/dist/src/table/index.js +9 -8
  95. package/dist/src/table/td.d.ts +0 -4
  96. package/dist/src/table/td.js +17 -7
  97. package/dist/src/table/tr.d.ts +2 -4
  98. package/dist/src/table/tr.js +2 -3
  99. package/dist/src/table/trBase.d.ts +0 -2
  100. package/dist/src/table/trBase.js +2 -2
  101. package/dist/src/tagPair/ext.js +5 -7
  102. package/dist/src/tagPair/include.d.ts +7 -5
  103. package/dist/src/tagPair/include.js +106 -56
  104. package/dist/src/tagPair/index.d.ts +2 -4
  105. package/dist/src/tagPair/index.js +4 -4
  106. package/dist/src/transclude.d.ts +0 -2
  107. package/dist/src/transclude.js +15 -17
  108. package/dist/util/debug.js +10 -1
  109. package/dist/util/lint.js +3 -2
  110. package/dist/util/string.js +5 -5
  111. package/package.json +12 -12
package/README.en.md CHANGED
@@ -50,8 +50,8 @@ or
50
50
  <script src="//unpkg.com/wikiparser-node@browser/bundle/bundle.min.js"></script>
51
51
  ```
52
52
 
53
- For more browser extensions, please refer to the corresponding [documentation](https://github.com/bhsd-harry/wikiparser-node/wiki/Browser.en).
53
+ For more browser extensions, please refer to the corresponding [documentation](https://github.com/bhsd-harry/wikiparser-node/wiki/Browser-%28EN%29).
54
54
 
55
55
  # Usage
56
56
 
57
- Please refer to the [Wiki](https://github.com/bhsd-harry/wikiparser-node/wiki/Home.en).
57
+ Please refer to the [Wiki](https://github.com/bhsd-harry/wikiparser-node/wiki/Home-%28EN%29).
@@ -38,7 +38,7 @@ function occupied(layout, i, oneRow, cells) {
38
38
  if (rowLayout) {
39
39
  return rowLayout.map(({ row, column }, j) => row === i && (!oneRow || cells[column]?.rowspan === 1) ? j : undefined).filter((j) => j !== undefined);
40
40
  }
41
- throw new RangeError(`表格没有第 ${i} 行!`);
41
+ throw new RangeError(`The table layout does not contain row ${i}!`);
42
42
  }
43
43
  /**
44
44
  * 设置表格格式
@@ -145,7 +145,7 @@ index_2.TableToken.prototype.insertTableCell =
145
145
  const { x, y } = coords;
146
146
  rawCoords = this.toRawCoords(coords);
147
147
  if (!rawCoords?.start) {
148
- throw new RangeError(`指定的坐标不是单元格起始点:(${x}, ${y})`);
148
+ throw new RangeError(`The specified coordinates are not the starting point of any cell: (${x}, ${y})`);
149
149
  }
150
150
  }
151
151
  else {
@@ -206,7 +206,7 @@ index_2.TableToken.prototype.insertTableCol =
206
206
  function (x, inner, subtype, attr) {
207
207
  const layout = this.getLayout(), rowLength = layout.map(({ length }) => length), minCol = Math.min(...rowLength);
208
208
  if (x > minCol) {
209
- throw new RangeError(`表格第 ${rowLength.indexOf(minCol)} 行仅有 ${minCol} 列!`);
209
+ throw new RangeError(`Row ${rowLength.indexOf(minCol)} has only ${minCol} column(s)!`);
210
210
  }
211
211
  const token = (0, td_1.createTd)(inner, subtype, attr, this.getAttribute('include'), this.getAttribute('config'));
212
212
  for (let i = 0; i < layout.length; i++) {
@@ -275,7 +275,7 @@ index_2.TableToken.prototype.mergeCells =
275
275
  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)));
276
276
  if ([...layout[ymin - 1] ?? [], ...layout[ymax] ?? []].some(coords => set.has(coords))
277
277
  || layout.some(rowLayout => set.has(rowLayout[xmin - 1]) || set.has(rowLayout[xmax]))) {
278
- throw new RangeError('待合并区域与外侧区域有重叠!');
278
+ throw new RangeError('The area to be merged overlaps with the outer area!');
279
279
  }
280
280
  const corner = layout[ymin][xmin], rows = this.getAllRows(), cornerCell = rows[corner.row].getNthCol(corner.column);
281
281
  cornerCell.rowspan = ymax - ymin;
@@ -318,7 +318,8 @@ index_2.TableToken.prototype.split =
318
318
  this.insertTableCell('', { x: i, y: j }, subtype, attr);
319
319
  }
320
320
  catch (e) {
321
- if (e instanceof RangeError && e.message.startsWith('指定的坐标不是单元格起始点:')) {
321
+ if (e instanceof RangeError
322
+ && e.message.startsWith('The specified coordinates are not the starting point of a cell: ')) {
322
323
  break;
323
324
  }
324
325
  throw e;
@@ -386,7 +387,7 @@ index_2.TableToken.prototype.moveTableRowBefore =
386
387
  }
387
388
  catch (e) {
388
389
  if (e instanceof assert.AssertionError) {
389
- throw new RangeError(`第 ${y} 行与第 ${before} 行的构造不同,无法移动!`);
390
+ throw new RangeError(`The structure of row ${y} is different from that of row ${before}, so it cannot be moved!`);
390
391
  }
391
392
  throw e;
392
393
  }
@@ -412,7 +413,7 @@ index_2.TableToken.prototype.moveTableRowAfter =
412
413
  }
413
414
  catch (e) {
414
415
  if (e instanceof assert.AssertionError) {
415
- throw new RangeError(`第 ${y} 行与第 ${after} 行的构造不同,无法移动!`);
416
+ throw new RangeError(`The structure of row ${y} is different from that of row ${after}, so it cannot be moved!`);
416
417
  }
417
418
  throw e;
418
419
  }
@@ -442,7 +443,7 @@ index_2.TableToken.prototype.moveCol =
442
443
  function (x, reference, after) {
443
444
  const layout = this.getLayout();
444
445
  if (layout.some(rowLayout => isStartCol(rowLayout, x) !== isStartCol(rowLayout, reference, after))) {
445
- throw new RangeError(`第 ${x} 列与第 ${reference} 列的构造不同,无法移动!`);
446
+ throw new RangeError(`The structure of column ${x} is different from that of column ${reference}, so it cannot be moved!`);
446
447
  }
447
448
  const setX = new WeakSet(), setRef = new WeakSet(), rows = this.getAllRows();
448
449
  for (let i = 0; i < layout.length; i++) {
@@ -29,7 +29,7 @@ index_1.Token.prototype.createElement =
29
29
  // @ts-expect-error abstract class
30
30
  return debug_1.Shadow.run(() => new ext_1.ExtToken(tagName, '', undefined, selfClosing ? undefined : '', config));
31
31
  }
32
- else if (config.html.flat().includes(tagName)) {
32
+ else if (config.html.some(tags => tags.includes(tagName))) {
33
33
  return debug_1.Shadow.run(() => {
34
34
  // @ts-expect-error abstract class
35
35
  const attr = new attributes_1.AttributesToken(undefined, 'html-attrs', tagName, config);
@@ -37,7 +37,7 @@ index_1.Token.prototype.createElement =
37
37
  return new html_1.HtmlToken(tagName, attr, Boolean(closing), Boolean(selfClosing), config);
38
38
  });
39
39
  }
40
- throw new RangeError(`非法的标签名:${tagName}`);
40
+ throw new RangeError(`Invalid tag name: ${tagName}`);
41
41
  };
42
42
  index_1.Token.prototype.caretPositionFromIndex =
43
43
  /** @implements */
@@ -110,8 +110,11 @@ index_1.Token.prototype.findEnclosingHtml =
110
110
  function (tag) {
111
111
  tag = tag?.toLowerCase();
112
112
  const { html } = this.getAttribute('config'), normalTags = new Set(html[0]), voidTags = new Set(html[2]);
113
- if (tag !== undefined && !html.slice(0, 2).flat().includes(tag)) {
114
- throw new RangeError(`非法的标签或空标签:${tag}`);
113
+ if (html[2].includes(tag)) {
114
+ throw new RangeError(`Void tag: ${tag}`);
115
+ }
116
+ else if (tag !== undefined && !html.slice(0, 2).some(tags => tags.includes(tag))) {
117
+ throw new RangeError(`Invalid tag name: ${tag}`);
115
118
  }
116
119
  const { parentNode } = this;
117
120
  if (!parentNode) {
@@ -155,7 +158,7 @@ index_1.Token.prototype.findEnclosingHtml =
155
158
  index_1.Token.prototype.redoQuotes =
156
159
  /** @implements */
157
160
  function () {
158
- const acceptable = this.getAttribute('acceptable');
161
+ const acceptable = this.getAcceptable();
159
162
  if (acceptable && !('QuoteToken' in acceptable)) {
160
163
  return;
161
164
  }
@@ -23,7 +23,7 @@ transclude_1.TranscludeToken.prototype.setValue =
23
23
  /** @implements */
24
24
  function (key, value) {
25
25
  if (!this.isTemplate()) {
26
- throw new Error('setValue 方法仅供模板使用!');
26
+ throw new Error('TranscludeToken.setValue method is only for templates!');
27
27
  }
28
28
  const arg = this.getArg(key);
29
29
  if (arg) {
@@ -42,7 +42,7 @@ transclude_1.TranscludeToken.prototype.replaceTemplate =
42
42
  /** @implements */
43
43
  function (title) {
44
44
  if (this.type === 'magic-word') {
45
- throw new Error('replaceTemplate 方法仅用于更换模板!');
45
+ throw new Error('TranscludeToken.replaceTemplate method is only for templates!');
46
46
  }
47
47
  const { childNodes } = index_1.default.parse(title, this.getAttribute('include'), 2, this.getAttribute('config'));
48
48
  this.firstChild.replaceChildren(...childNodes);
@@ -51,7 +51,7 @@ transclude_1.TranscludeToken.prototype.replaceModule =
51
51
  /** @implements */
52
52
  function (title) {
53
53
  if (this.type !== 'magic-word' || this.name !== 'invoke') {
54
- throw new Error('replaceModule 方法仅用于更换模块!');
54
+ throw new Error('TranscludeToken.replaceModule method is only for modules!');
55
55
  }
56
56
  const config = this.getAttribute('config');
57
57
  if (this.length === 1) {
@@ -67,10 +67,10 @@ transclude_1.TranscludeToken.prototype.replaceFunction =
67
67
  /** @implements */
68
68
  function (func) {
69
69
  if (this.type !== 'magic-word' || this.name !== 'invoke') {
70
- throw new Error('replaceModule 方法仅用于更换模块!');
70
+ throw new Error('TranscludeToken.replaceModule method is only for modules!');
71
71
  }
72
72
  else if (this.length < 2) {
73
- throw new Error('尚未指定模块名称!');
73
+ throw new Error('No module name specified!');
74
74
  }
75
75
  const config = this.getAttribute('config');
76
76
  if (this.length === 2) {
@@ -129,8 +129,10 @@ transclude_1.TranscludeToken.prototype.fixDuplication =
129
129
  }
130
130
  else if (aggressive && (anonCount ? /\D\d+$/u : /(?:^|\D)\d+$/u).test(key)) {
131
131
  let last;
132
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
133
+ /^a\d+$/u;
132
134
  // eslint-disable-next-line es-x/no-regexp-lookbehind-assertions
133
- const str = key.slice(0, -/(?<!\d)\d+$/u.exec(key)[0].length), regex = new RegExp(`^${(0, string_1.escapeRegExp)(str)}\\d+$`, 'u'), series = this.getAllArgs().filter(({ name }) => regex.test(name)), ordered = series.every(({ name }, i) => {
135
+ const str = key.slice(0, -/(?<!\d)\d+$/u.exec(key)[0].length), regex = new RegExp(String.raw `^${(0, string_1.escapeRegExp)(str)}\d+$`, 'u'), series = this.getAllArgs().filter(({ name }) => regex.test(name)), ordered = series.every(({ name }, i) => {
134
136
  const j = Number(name.slice(str.length)), cmp = j <= i + 1 && (i === 0 || j >= last || name === key);
135
137
  last = j;
136
138
  return cmp;
@@ -151,10 +153,10 @@ transclude_1.TranscludeToken.prototype.fixDuplication =
151
153
  index_1.default.error(`${this.type === 'template'
152
154
  ? this.name
153
155
  : this.normalizeTitle(this.childNodes[1].text(), 828)
154
- .title} 还留有 ${remaining} 个重复的 ${key} 参数:${[...this.getArgs(key)].map(arg => {
156
+ .title} still has ${remaining} duplicated ${key} parameters:\n${[...this.getArgs(key)].map(arg => {
155
157
  const { top, left } = arg.getBoundingClientRect();
156
- return `第 ${String(top)} 行第 ${String(left)} 列`;
157
- }).join('')}`);
158
+ return `Line ${String(top)} Column ${String(left)}`;
159
+ }).join('\n')}`);
158
160
  duplicatedKeys.push(key);
159
161
  continue;
160
162
  }
@@ -175,7 +177,7 @@ transclude_1.TranscludeToken.prototype.escapeTables =
175
177
  }
176
178
  const { firstChild, length } = index_1.default.parse(`{{${parsed.toString()}}}`, include, undefined, config);
177
179
  if (length !== 1 || !(firstChild instanceof transclude_1.TranscludeToken)) {
178
- throw new Error('转义表格失败!');
180
+ throw new Error('Failed to escape tables!');
179
181
  }
180
182
  this.safeReplaceWith(firstChild);
181
183
  return firstChild;
package/dist/base.d.ts CHANGED
@@ -61,6 +61,8 @@ interface AstElement extends AstNode {
61
61
  export interface Parser {
62
62
  config: Config | string;
63
63
  i18n: Record<string, string> | string | undefined;
64
+ /** 获取当前的解析设置 */
65
+ getConfig(): Config;
64
66
  /**
65
67
  * 解析wikitext
66
68
  * @param include 是否嵌入
package/dist/index.d.ts CHANGED
@@ -13,11 +13,8 @@ declare interface Parser extends ParserBase {
13
13
  * @param title 标题(含或不含命名空间前缀)
14
14
  * @param defaultNs 命名空间
15
15
  * @param include 是否嵌入
16
- * @param halfParsed 是否是半解析状态
17
- * @param decode 是否需要解码
18
- * @param selfLink 是否允许selfLink
19
16
  */
20
- normalizeTitle(title: string, defaultNs?: number, include?: boolean, config?: Config, halfParsed?: boolean, decode?: boolean, selfLink?: boolean): Title;
17
+ normalizeTitle(title: string, defaultNs?: number, include?: boolean, config?: Config): Title;
21
18
  parse(wikitext: string, include?: boolean, maxStage?: number, config?: Config): Token;
22
19
  /**
23
20
  * 是否是跨维基链接
@@ -28,6 +25,7 @@ declare interface Parser extends ParserBase {
28
25
  declare const Parser: Parser;
29
26
  // @ts-expect-error mixed export styles
30
27
  export = Parser;
28
+ export default Parser;
31
29
  export type { Config, LintError };
32
30
  export type * from './internal';
33
31
  declare global { type Acceptable = unknown; }
package/dist/index.js CHANGED
@@ -144,7 +144,7 @@ const Parser = {
144
144
  },
145
145
  /** @implements */
146
146
  async clearCache() {
147
- const promise = (0, diff_1.cmd)('npm', ['run', 'build']), entries = [
147
+ const promise = (0, diff_1.cmd)('npm', ['run', 'build:core']), entries = [
148
148
  ...Object.entries(constants_1.classes),
149
149
  ...Object.entries(constants_1.mixins),
150
150
  ...Object.entries(constants_1.parsers),
@@ -166,8 +166,10 @@ const Parser = {
166
166
  },
167
167
  /** @implements */
168
168
  isInterwiki(title, { interwiki } = Parser.getConfig()) {
169
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
170
+ /^(zh|en)\s*:/diu;
169
171
  return interwiki.length > 0
170
- ? new RegExp(`^(${interwiki.join('|')})\\s*:`, 'diu')
172
+ ? new RegExp(String.raw `^(${interwiki.join('|')})\s*:`, 'diu')
171
173
  .exec(title.replace(/_/gu, ' ').replace(/^\s*:?\s*/u, ''))
172
174
  : null;
173
175
  },
@@ -215,4 +217,5 @@ for (const key in Parser) {
215
217
  }
216
218
  }
217
219
  Object.defineProperties(Parser, def);
220
+ exports.default = Parser;
218
221
  module.exports = Parser;
@@ -27,11 +27,6 @@ export declare abstract class AstElement extends AstNode {
27
27
  /** 内部宽度 */
28
28
  get clientWidth(): number | undefined;
29
29
  constructor();
30
- /**
31
- * 可见部分
32
- * @param separator 子节点间的连接符
33
- */
34
- text(separator?: string): string;
35
30
  /** 合并相邻的文本子节点 */
36
31
  normalize(): void;
37
32
  /**
@@ -21,7 +21,7 @@ const toCase = (val, i) => i ? val.toLowerCase() : val;
21
21
  * @param str 表达式
22
22
  * @param i 待检查的下标
23
23
  */
24
- const nth = (str, i) => new ranges_1.Ranges(str.split(',')).applyTo(i + 1).includes(i);
24
+ const nth = (str, i) => new ranges_1.Ranges(str).applyTo(i + 1).includes(i);
25
25
  /**
26
26
  * 检测:lang()伪选择器
27
27
  * @param node 节点
@@ -30,7 +30,7 @@ const nth = (str, i) => new ranges_1.Ranges(str.split(',')).applyTo(i + 1).inclu
30
30
  */
31
31
  const matchesLang = ({ attributes }, regex) => {
32
32
  const lang = attributes?.['lang'];
33
- return typeof lang === 'string' && regex.test(lang);
33
+ return lang === undefined ? undefined : typeof lang === 'string' && regex.test(lang);
34
34
  };
35
35
  const primitives = new Set(['string', 'number', 'boolean', 'undefined']);
36
36
  /* NOT FOR BROWSER END */
@@ -88,10 +88,7 @@ class AstElement extends node_1.AstNode {
88
88
  this.seal('name');
89
89
  }
90
90
  /* NOT FOR BROWSER END */
91
- /**
92
- * 可见部分
93
- * @param separator 子节点间的连接符
94
- */
91
+ /** @private */
95
92
  text(separator) {
96
93
  return (0, string_1.text)(this.childNodes, separator);
97
94
  }
@@ -145,10 +142,10 @@ class AstElement extends node_1.AstNode {
145
142
  insertAt(node, i = this.length) {
146
143
  /* NOT FOR BROWSER */
147
144
  if (node.contains(this)) {
148
- throw new RangeError('不能插入祖先节点!');
145
+ throw new RangeError('Cannot insert an ancestor node!');
149
146
  }
150
- if (this.childNodes.includes(node)) {
151
- throw new RangeError('不能插入子节点!');
147
+ else if (this.childNodes.includes(node)) {
148
+ throw new RangeError('Cannot insert its own child node!');
152
149
  }
153
150
  this.verifyChild(i, 1);
154
151
  node.parentNode?.removeChild(node);
@@ -275,7 +272,7 @@ class AstElement extends node_1.AstNode {
275
272
  return data;
276
273
  }
277
274
  /* NOT FOR BROWSER */
278
- throw new RangeError(`第 ${i} 个子节点是 ${oldText.constructor.name}!`);
275
+ throw new RangeError(`The child node at position ${i} is ${oldText.constructor.name}!`);
279
276
  }
280
277
  /** @private */
281
278
  toString(separator = '') {
@@ -310,17 +307,14 @@ class AstElement extends node_1.AstNode {
310
307
  };
311
308
  for (let i = 0, cur = start + this.getAttribute('padding'); i < this.length; i++) {
312
309
  const child = this.childNodes[i], { length } = child.toString();
313
- if (child.type === 'text') {
314
- json.childNodes.push({ data: child.data, range: [cur, cur + length] });
315
- }
316
- else {
317
- json.childNodes.push(child.json(undefined, cur));
318
- }
310
+ json.childNodes.push(child.type === 'text'
311
+ ? { data: child.data, range: [cur, cur + length] }
312
+ : child.json(undefined, cur));
319
313
  cur += length + this.getGaps(i);
320
314
  }
321
315
  /* NOT FOR BROWSER */
322
316
  if (typeof file === 'string') {
323
- fs.writeFileSync(path.join(__dirname.slice(0, -4), '..', 'printed', file + (file.endsWith('.json') ? '' : '.json')), JSON.stringify(json, null, 2));
317
+ fs.writeFileSync(path.join(__dirname, '..', '..', 'printed', file + (file.endsWith('.json') ? '' : '.json')), JSON.stringify(json, null, 2));
324
318
  }
325
319
  /* NOT FOR BROWSER END */
326
320
  return json;
@@ -332,8 +326,24 @@ class AstElement extends node_1.AstNode {
332
326
  if (!parentNode) {
333
327
  return undefined;
334
328
  }
335
- const { childNodes, fixed } = parentNode, protectedIndices = parentNode.getAttribute('protectedChildren').applyTo(childNodes);
336
- return fixed || protectedIndices.includes(childNodes.indexOf(this));
329
+ const { childNodes, fixed } = parentNode;
330
+ return fixed
331
+ || parentNode.getAttribute('protectedChildren').applyTo(childNodes)
332
+ .includes(childNodes.indexOf(this));
333
+ }
334
+ /**
335
+ * 获取属性
336
+ * @param key 属性键
337
+ */
338
+ #getAttr(key) {
339
+ if (typeof this.getAttr === 'function') {
340
+ const attr = this.getAttr(key);
341
+ if (attr !== undefined) {
342
+ return attr;
343
+ }
344
+ }
345
+ const val = this.getAttribute(key);
346
+ return val instanceof RegExp ? val.source : val;
337
347
  }
338
348
  /**
339
349
  * 检查是否符合属性选择器
@@ -348,27 +358,17 @@ class AstElement extends node_1.AstNode {
348
358
  if (!(key in this) && (!isAttr || !this.hasAttr(key))) {
349
359
  return equal === '!=';
350
360
  }
351
- const v = toCase(val, i);
352
- let thisVal = this.getAttribute(key);
353
- if (isAttr) {
354
- const attr = this.getAttr(key);
355
- if (attr !== undefined) {
356
- thisVal = attr === true ? '' : attr;
357
- }
358
- }
361
+ const v = toCase(val, i), thisVal = this.#getAttr(key);
359
362
  if (!equal) {
360
363
  return thisVal !== undefined && thisVal !== false;
361
364
  }
362
- else if (thisVal instanceof RegExp) {
363
- thisVal = thisVal.source;
364
- }
365
365
  if (equal === '~=') {
366
366
  const thisVals = typeof thisVal === 'string' ? thisVal.split(/\s/u) : thisVal;
367
367
  return Boolean(thisVals?.[Symbol.iterator])
368
368
  && [...thisVals].some(w => typeof w === 'string' && toCase(w, i) === v);
369
369
  }
370
370
  else if (!primitives.has(typeof thisVal) && !(thisVal instanceof title_1.Title)) {
371
- throw new RangeError(`复杂属性 ${key} 不能用于选择器!`);
371
+ throw new RangeError(`The complex attribute ${key} cannot be used in a selector!`);
372
372
  }
373
373
  const stringVal = toCase(String(thisVal), i);
374
374
  switch (equal) {
@@ -430,7 +430,7 @@ class AstElement extends node_1.AstNode {
430
430
  || type === 'free-ext-link'
431
431
  || type === 'magic-link'
432
432
  || type === 'ext-link'
433
- || (type === 'file' || type === 'gallery-image' && link);
433
+ || (type === 'file' || type === 'gallery-image') && link;
434
434
  case ':local-link':
435
435
  return (type === 'link' || type === 'file' || type === 'gallery-image')
436
436
  && link instanceof title_1.Title
@@ -469,24 +469,31 @@ class AstElement extends node_1.AstNode {
469
469
  case 'has':
470
470
  return Boolean(this.querySelector(s));
471
471
  case 'lang': {
472
- const regex = new RegExp(`^${s}(?:-|$)`, 'u');
473
- return matchesLang(this, regex)
474
- || this.getAncestors().some(ancestor => matchesLang(ancestor, regex));
472
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
473
+ /^zh(?:-|$)/iu;
474
+ const regex = new RegExp(`^${s}(?:-|$)`, 'iu');
475
+ for (let node = this; node; node = node.parentNode) {
476
+ const result = matchesLang(node, regex);
477
+ if (result !== undefined) {
478
+ return result;
479
+ }
480
+ }
481
+ return false;
475
482
  }
476
483
  case 'regex': {
477
484
  const mt = /^([^,]+),\s*\/(.+)\/([a-z]*)$/u.exec(s);
478
485
  if (!mt) {
479
- throw new SyntaxError('错误的伪选择器用法。请使用形如 ":regex(\'attr, /re/i\')" 的格式。');
486
+ throw new SyntaxError(`Wrong usage of the regex pseudo-selector. Use ":regex('attr, /re/i')" format.`);
480
487
  }
481
488
  try {
482
- return new RegExp(mt[2], mt[3]).test(String(this.getAttribute(mt[1].trim())));
489
+ return new RegExp(mt[2], mt[3]).test(String(this.#getAttr(mt[1].trim())));
483
490
  }
484
491
  catch {
485
- throw new SyntaxError(`错误的正则表达式:/${mt[2]}/${mt[3]}`);
492
+ throw new SyntaxError(`Invalid regular expression: /${mt[2]}/${mt[3]}`);
486
493
  }
487
494
  }
488
495
  default:
489
- throw new SyntaxError(`未定义的伪选择器:${pseudo}`);
496
+ throw new SyntaxError(`Undefined pseudo-selector: ${pseudo}`);
490
497
  }
491
498
  });
492
499
  }
@@ -19,7 +19,7 @@ export declare abstract class AstNode implements AstNodeBase {
19
19
  type: TokenTypes | 'text';
20
20
  data?: string | undefined;
21
21
  readonly childNodes: readonly AstNodes[];
22
- abstract text(): string;
22
+ text(): string;
23
23
  lint(): LintError[];
24
24
  print(): string;
25
25
  /** 首位子节点 */
package/dist/lib/node.js CHANGED
@@ -130,13 +130,13 @@ class AstNode {
130
130
  }
131
131
  /** 字体样式 */
132
132
  get font() {
133
- const { parentNode } = this, acceptable = parentNode?.getAttribute('acceptable');
133
+ const { parentNode } = this, acceptable = parentNode?.getAcceptable();
134
134
  if (!parentNode || acceptable && !('QuoteToken' in acceptable)) {
135
135
  return { bold: false, italic: false };
136
136
  }
137
- const { childNodes } = parentNode, index = childNodes.indexOf(this), isQuote = (0, debug_1.isToken)('quote');
138
- let bold = false, italic = false;
139
- for (let i = index - 1; i >= 0; i--) {
137
+ const { childNodes, type } = parentNode, isQuote = (0, debug_1.isToken)('quote');
138
+ let { bold = false, italic = false } = type === 'ext-link-text' && parentNode.parentNode || {};
139
+ for (let i = childNodes.indexOf(this) - 1; i >= 0; i--) {
140
140
  const child = childNodes[i];
141
141
  if (isQuote(child)) {
142
142
  bold = child.bold !== bold;
@@ -168,7 +168,7 @@ class AstNode {
168
168
  /* NOT FOR BROWSER */
169
169
  }
170
170
  else if (key === 'optional') {
171
- return new Set(this.#optional);
171
+ return this.#optional;
172
172
  /* NOT FOR BROWSER END */
173
173
  }
174
174
  return this[key];
@@ -264,7 +264,7 @@ class AstNode {
264
264
  }
265
265
  /** @private */
266
266
  constructorError(msg) {
267
- throw new Error(`${this.constructor.name} ${msg}!`);
267
+ throw new Error(`${this.constructor.name} ${msg}!`);
268
268
  }
269
269
  /**
270
270
  * 是否是全同节点
@@ -292,7 +292,7 @@ class AstNode {
292
292
  #insertAdjacent(nodes, offset) {
293
293
  const { parentNode } = this;
294
294
  if (!parentNode) {
295
- throw new Error('The node has no parent!');
295
+ throw new Error('There is no parent node!');
296
296
  }
297
297
  const i = parentNode.childNodes.indexOf(this) + offset;
298
298
  for (let j = 0; j < nodes.length; j++) {
@@ -336,7 +336,7 @@ class AstNode {
336
336
  verifyChild(i, addition = 0) {
337
337
  const { childNodes: { length } } = this;
338
338
  if (i < -length || i >= length + addition) {
339
- throw new RangeError(`The ${i}th child does not exist!`);
339
+ throw new RangeError(`The child node at position ${i} does not exist!`);
340
340
  }
341
341
  }
342
342
  /**
@@ -436,7 +436,7 @@ class AstNode {
436
436
  return 1;
437
437
  }
438
438
  else if (this.getRootNode() !== other.getRootNode()) {
439
- throw new RangeError('不在同一个语法树!');
439
+ throw new RangeError('Nodes to be compared are not in the same document!');
440
440
  }
441
441
  const aAncestors = [...this.getAncestors().reverse(), this], bAncestors = [...other.getAncestors().reverse(), other], depth = aAncestors.findIndex((ancestor, i) => bAncestors[i] !== ancestor), { childNodes } = aAncestors[depth - 1];
442
442
  return childNodes.indexOf(aAncestors[depth]) - childNodes.indexOf(bAncestors[depth]);
@@ -0,0 +1,18 @@
1
+ import type { AstNodes, Position } from './node';
2
+ /** 节点位置 */
3
+ export declare class BoundingRect {
4
+ #private;
5
+ readonly token: AstNodes;
6
+ readonly start: number;
7
+ /** 起点行 */
8
+ get top(): number;
9
+ /** 起点列 */
10
+ get left(): number;
11
+ /**
12
+ * @param token 节点
13
+ * @param start 起点
14
+ */
15
+ constructor(token: AstNodes, start: number);
16
+ /** 计算位置 */
17
+ getPosition(): Position;
18
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BoundingRect = void 0;
4
+ const constants_1 = require("../util/constants");
5
+ /** 节点位置 */
6
+ class BoundingRect {
7
+ #pos;
8
+ token;
9
+ start;
10
+ /** 起点行 */
11
+ get top() {
12
+ this.#pos ??= this.getPosition();
13
+ return this.#pos.top;
14
+ }
15
+ /** 起点列 */
16
+ get left() {
17
+ this.#pos ??= this.getPosition();
18
+ return this.#pos.left;
19
+ }
20
+ /**
21
+ * @param token 节点
22
+ * @param start 起点
23
+ */
24
+ constructor(token, start) {
25
+ this.token = token;
26
+ this.start = start;
27
+ }
28
+ /** 计算位置 */
29
+ getPosition() {
30
+ return this.token.getRootNode().posFromIndex(this.start);
31
+ }
32
+ }
33
+ exports.BoundingRect = BoundingRect;
34
+ constants_1.classes['BoundingRect'] = __filename;
package/dist/lib/text.js CHANGED
@@ -6,16 +6,11 @@ const debug_1 = require("../util/debug");
6
6
  const string_1 = require("../util/string");
7
7
  const index_1 = require("../index");
8
8
  const node_1 = require("./node");
9
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
9
+ /* eslint-disable @typescript-eslint/no-unused-expressions */
10
10
  /<\s*(?:\/\s*)?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*?\])|((?:^|\])[^[]*?)\]+|https?[:/]\/+/giu;
11
- const source = '<\\s*(?:\\/\\s*)?([a-z]\\w*)' // 疑似标签
12
- + '|'
13
- + '\\{+|\\}+' // `{`、`}`
14
- + '|'
15
- + '\\[{2,}|\\[(?![^[]*?\\])' // `[`
16
- + '|'
17
- + '((?:^|\\])[^[]*?)\\]+', // `]`
18
- errorSyntax = new RegExp(`${source}|https?[:/]\\/+`, 'giu'), errorSyntaxUrl = new RegExp(source, 'giu'), extImage = new RegExp(`^https?:\\/\\/${string_1.extUrlCharFirst}${string_1.extUrlChar}\\.(?:gif|png|jpg|jpeg)$`, 'iu'), regexes = {
11
+ /^https?:\/\/(?:\[[\da-f:.]+\]|[^[\]<>"\t\n\p{Zs}])[^[\]<>"\t\n\p{Zs}]*\.(?:gif|png|jpg|jpeg)$/iu;
12
+ /* eslint-enable @typescript-eslint/no-unused-expressions */
13
+ const source = String.raw `<\s*(?:/\s*)?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*?\])|((?:^|\])[^[]*?)\]+`, 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'), regexes = {
19
14
  '[': /[[\]]/u,
20
15
  '{': /[{}]/u,
21
16
  ']': /[[\]](?=[^[\]]*$)/u,
@@ -114,7 +109,7 @@ class AstText extends node_1.AstNode {
114
109
  lint(start = this.getAbsoluteIndex(), errorRegex) {
115
110
  const { data, parentNode, nextSibling, previousSibling } = this;
116
111
  if (!parentNode) {
117
- throw new Error('无法对孤立文本节点进行语法分析!');
112
+ throw new Error('An isolated text node cannot be linted!');
118
113
  }
119
114
  const { type, name, parentNode: grandparent } = parentNode;
120
115
  let isHtmlAttrVal = false;
@@ -312,11 +307,11 @@ class AstText extends node_1.AstNode {
312
307
  */
313
308
  splitText(offset) {
314
309
  if (offset > this.length || offset < -this.length) {
315
- throw new RangeError(`错误的断开位置:${offset}`);
310
+ throw new RangeError(`Wrong offset to split: ${offset}`);
316
311
  }
317
312
  const { parentNode, data } = this;
318
313
  if (!parentNode) {
319
- throw new Error('待分裂的文本节点没有父节点!');
314
+ throw new Error('The text node to be split has no parent node!');
320
315
  }
321
316
  const newText = new AstText(data.slice(offset));
322
317
  (0, debug_1.setChildNodes)(parentNode, parentNode.childNodes.indexOf(this) + 1, 0, [newText]);
@@ -329,7 +324,7 @@ class AstText extends node_1.AstNode {
329
324
  return super.getRelativeIndex();
330
325
  }
331
326
  else if (j < 0 || j > this.length) {
332
- throw new RangeError('超出文本长度范围!');
327
+ throw new RangeError('Exceeding the text length range!');
333
328
  }
334
329
  return j;
335
330
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.attributesParent = void 0;
4
+ const debug_1 = require("../util/debug");
4
5
  const constants_1 = require("../util/constants");
5
6
  /**
6
7
  * 子节点含有AttributesToken的类
@@ -74,7 +75,7 @@ const attributesParent = (i = 0) => (constructor, _) => {
74
75
  this.#attributesChild.toggleAttr(key, force);
75
76
  }
76
77
  }
77
- Object.defineProperty(AttributesParent, 'name', { value: constructor.name });
78
+ (0, debug_1.mixin)(AttributesParent, constructor);
78
79
  return AttributesParent;
79
80
  };
80
81
  exports.attributesParent = attributesParent;