wikiparser-node 1.3.4 → 1.3.6

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 (74) hide show
  1. package/dist/addon/table.js +13 -19
  2. package/dist/index.d.ts +1 -0
  3. package/dist/lib/element.d.ts +0 -1
  4. package/dist/lib/element.js +4 -13
  5. package/dist/lib/node.js +4 -0
  6. package/dist/lib/ranges.js +2 -1
  7. package/dist/lib/text.js +4 -5
  8. package/dist/mixin/attributesParent.js +2 -7
  9. package/dist/mixin/fixed.js +3 -10
  10. package/dist/mixin/syntax.js +1 -3
  11. package/dist/parser/braces.js +9 -2
  12. package/dist/parser/commentAndExt.js +18 -10
  13. package/dist/parser/list.js +11 -4
  14. package/dist/parser/selector.js +19 -20
  15. package/dist/parser/table.js +28 -20
  16. package/dist/src/arg.d.ts +0 -2
  17. package/dist/src/arg.js +2 -4
  18. package/dist/src/attribute.d.ts +1 -0
  19. package/dist/src/attribute.js +3 -3
  20. package/dist/src/attributes.d.ts +3 -1
  21. package/dist/src/attributes.js +21 -18
  22. package/dist/src/converter.d.ts +1 -0
  23. package/dist/src/converterRule.d.ts +1 -5
  24. package/dist/src/converterRule.js +3 -7
  25. package/dist/src/extLink.d.ts +1 -0
  26. package/dist/src/gallery.d.ts +0 -1
  27. package/dist/src/gallery.js +1 -2
  28. package/dist/src/heading.d.ts +3 -1
  29. package/dist/src/heading.js +2 -2
  30. package/dist/src/hidden.d.ts +1 -0
  31. package/dist/src/html.d.ts +3 -0
  32. package/dist/src/html.js +0 -1
  33. package/dist/src/imageParameter.d.ts +0 -1
  34. package/dist/src/imageParameter.js +0 -2
  35. package/dist/src/imagemap.d.ts +0 -1
  36. package/dist/src/imagemap.js +2 -3
  37. package/dist/src/imagemapLink.d.ts +2 -0
  38. package/dist/src/index.d.ts +1 -8
  39. package/dist/src/index.js +17 -17
  40. package/dist/src/link/file.js +2 -1
  41. package/dist/src/link/galleryImage.d.ts +1 -0
  42. package/dist/src/link/index.js +1 -1
  43. package/dist/src/magicLink.d.ts +1 -2
  44. package/dist/src/magicLink.js +2 -3
  45. package/dist/src/nested.d.ts +0 -1
  46. package/dist/src/nested.js +1 -2
  47. package/dist/src/nowiki/base.d.ts +1 -0
  48. package/dist/src/nowiki/comment.d.ts +1 -0
  49. package/dist/src/nowiki/dd.js +1 -1
  50. package/dist/src/nowiki/doubleUnderscore.d.ts +2 -0
  51. package/dist/src/nowiki/hr.d.ts +2 -0
  52. package/dist/src/nowiki/list.d.ts +1 -0
  53. package/dist/src/nowiki/listBase.d.ts +1 -0
  54. package/dist/src/nowiki/noinclude.d.ts +1 -1
  55. package/dist/src/nowiki/noinclude.js +1 -2
  56. package/dist/src/nowiki/quote.d.ts +1 -0
  57. package/dist/src/parameter.d.ts +1 -0
  58. package/dist/src/parameter.js +1 -1
  59. package/dist/src/syntax.d.ts +2 -0
  60. package/dist/src/table/base.d.ts +2 -0
  61. package/dist/src/table/index.d.ts +0 -1
  62. package/dist/src/table/index.js +0 -1
  63. package/dist/src/table/td.d.ts +3 -0
  64. package/dist/src/table/td.js +9 -10
  65. package/dist/src/table/trBase.d.ts +0 -1
  66. package/dist/src/table/trBase.js +2 -10
  67. package/dist/src/tagPair/ext.d.ts +2 -0
  68. package/dist/src/tagPair/include.d.ts +1 -0
  69. package/dist/src/tagPair/index.d.ts +1 -0
  70. package/dist/src/transclude.js +9 -11
  71. package/dist/util/debug.js +39 -20
  72. package/dist/util/lint.js +12 -28
  73. package/dist/util/string.js +12 -22
  74. package/package.json +1 -1
@@ -39,6 +39,9 @@ const isStartCol = (rowLayout, i, oneCol = false) => {
39
39
  const coords = rowLayout[i];
40
40
  return rowLayout[i - 1] !== coords && (!oneCol || rowLayout[i + 1] !== coords);
41
41
  };
42
+ function occupied(layout, i, oneRow = false, cells) {
43
+ return layout[i].map(({ row, column }, j) => row === i && (!oneRow || cells[column].rowspan === 1) ? j : undefined).filter((j) => j !== undefined);
44
+ }
42
45
  /**
43
46
  * 设置表格格式
44
47
  * @param cells 单元格
@@ -52,9 +55,7 @@ const format = (cells, attr = {}, multi = false) => {
52
55
  token.setSyntax(attr);
53
56
  }
54
57
  else {
55
- for (const [k, v] of Object.entries(attr)) {
56
- token.setAttr(k, v);
57
- }
58
+ token.setAttr(attr);
58
59
  }
59
60
  }
60
61
  }
@@ -81,11 +82,10 @@ const fill = (y, rowToken, layout, maxCol, token) => {
81
82
  class Layout extends Array {
82
83
  /** 打印表格布局 */
83
84
  print() {
84
- const hBorders = new Array(this.length + 1).fill(undefined).map((_, i) => {
85
+ const hBorders = (0, debug_1.emptyArray)(this.length + 1, i => {
85
86
  const prev = this[i - 1] ?? [], next = this[i] ?? [];
86
- return new Array(Math.max(prev.length, next.length)).fill(undefined)
87
- .map((__, j) => prev[j] !== next[j]);
88
- }), vBorders = this.map(cur => new Array(cur.length + 1).fill(undefined).map((_, j) => cur[j - 1] !== cur[j]));
87
+ return (0, debug_1.emptyArray)(Math.max(prev.length, next.length), j => prev[j] !== next[j]);
88
+ }), vBorders = this.map(cur => (0, debug_1.emptyArray)(cur.length + 1, j => cur[j - 1] !== cur[j]));
89
89
  let out = '';
90
90
  for (let i = 0; i <= this.length; i++) {
91
91
  const hBorder = hBorders[i].map(Number), vBorderTop = (vBorders[i - 1] ?? []).map(Number), vBorderBottom = (vBorders[i] ?? []).map(Number),
@@ -111,7 +111,7 @@ table_1.TableToken.prototype.getNthCell =
111
111
  table_1.TableToken.prototype.getLayout =
112
112
  /** @implements */
113
113
  function (stop) {
114
- const rows = this.getAllRows(), { length } = rows, layout = new Layout(...new Array(length).fill(undefined).map(() => []));
114
+ const rows = this.getAllRows(), { length } = rows, layout = new Layout(...(0, debug_1.emptyArray)(length, () => []));
115
115
  for (let i = 0; i < length; i++) {
116
116
  if (i > (stop?.row ?? stop?.y ?? NaN)) {
117
117
  break;
@@ -250,9 +250,7 @@ table_1.TableToken.prototype.insertTableRow =
250
250
  function (y, attr = {}, inner, subtype = 'td', innerAttr = {}) {
251
251
  let reference = this.getNthRow(y, false, true);
252
252
  const token = debug_1.Shadow.run(() => new tr_1.TrToken('\n|-', undefined, this.getAttribute('config')));
253
- for (const [k, v] of Object.entries(attr)) {
254
- token.setAttr(k, v);
255
- }
253
+ token.setAttr(attr);
256
254
  if (reference?.type === 'table') { // `row === 0`且表格自身是有效行
257
255
  reference = this.prependTableRow();
258
256
  }
@@ -455,11 +453,9 @@ table_1.TableToken.prototype.replicateTableCol =
455
453
  table_1.TableToken.prototype.moveTableRowBefore =
456
454
  /** @implements */
457
455
  function (y, before) {
458
- const layout = this.getLayout(),
459
- /** @ignore */
460
- occupied = (i) => layout[i].map(({ row }, j) => row === i ? j : undefined).filter((j) => j !== undefined);
456
+ const layout = this.getLayout();
461
457
  try {
462
- assert.deepEqual(occupied(y), occupied(before));
458
+ assert.deepEqual(occupied(layout, y), occupied(layout, before));
463
459
  }
464
460
  catch (e) {
465
461
  if (e instanceof assert.AssertionError) {
@@ -483,11 +479,9 @@ table_1.TableToken.prototype.moveTableRowBefore =
483
479
  table_1.TableToken.prototype.moveTableRowAfter =
484
480
  /** @implements */
485
481
  function (y, after) {
486
- const layout = this.getLayout(), afterToken = this.getNthRow(after), cells = afterToken.childNodes.filter(child => child.type === 'td' && child.subtype !== 'caption'),
487
- /** @ignore */
488
- occupied = (i, oneRow = false) => layout[i].map(({ row, column }, j) => row === i && (!oneRow || cells[column].rowspan === 1) ? j : undefined).filter((j) => j !== undefined);
482
+ const layout = this.getLayout(), afterToken = this.getNthRow(after), cells = afterToken.childNodes.filter(child => child.type === 'td' && child.subtype !== 'caption');
489
483
  try {
490
- assert.deepEqual(occupied(y), occupied(after, true));
484
+ assert.deepEqual(occupied(layout, y), occupied(layout, after, true, cells));
491
485
  }
492
486
  catch (e) {
493
487
  if (e instanceof assert.AssertionError) {
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { Shadow } from './util/debug';
2
2
  import type { Config, LintError, Parser as ParserBase } from './base';
3
3
  import type { Title } from './lib/title';
4
4
  import type { Token } from './internal';
5
+ declare type log = (msg: string, ...args: unknown[]) => void;
5
6
  declare interface Parser extends ParserBase {
6
7
  readonly Shadow: typeof Shadow;
7
8
  conversionTable: Map<string, string>;
@@ -145,7 +145,6 @@ export declare abstract class AstElement extends AstNode {
145
145
  * @param reference 指定位置处的子节点
146
146
  */
147
147
  insertBefore(child: string, reference?: AstNodes): AstText;
148
- /** @ignore */
149
148
  insertBefore<T extends AstNodes>(child: T, reference?: AstNodes): T;
150
149
  /**
151
150
  * 输出AST
@@ -5,6 +5,7 @@ const fs = require("fs");
5
5
  const path = require("path");
6
6
  const string_1 = require("../util/string");
7
7
  const constants_1 = require("../util/constants");
8
+ const debug_1 = require("../util/debug");
8
9
  const selector_1 = require("../parser/selector");
9
10
  const ranges_1 = require("./ranges");
10
11
  const title_1 = require("./title");
@@ -130,10 +131,7 @@ class AstElement extends node_1.AstNode {
130
131
  */
131
132
  removeAt(i) {
132
133
  this.verifyChild(i);
133
- const childNodes = [...this.childNodes], [node] = childNodes.splice(i, 1);
134
- node.setAttribute('parentNode', undefined);
135
- this.setAttribute('childNodes', childNodes);
136
- return node;
134
+ return (0, debug_1.setChildNodes)(this, i, 1)[0];
137
135
  }
138
136
  /**
139
137
  * 插入子节点
@@ -143,19 +141,14 @@ class AstElement extends node_1.AstNode {
143
141
  */
144
142
  insertAt(node, i = this.length) {
145
143
  if (node.contains(this)) {
146
- Parser.error('不能插入祖先节点!', node);
147
144
  throw new RangeError('不能插入祖先节点!');
148
145
  }
149
- const childNodes = [...this.childNodes];
150
- if (childNodes.includes(node)) {
151
- Parser.error('不能插入子节点!', node);
146
+ if (this.childNodes.includes(node)) {
152
147
  throw new RangeError('不能插入子节点!');
153
148
  }
154
149
  this.verifyChild(i, 1);
155
150
  node.parentNode?.removeChild(node);
156
- node.setAttribute('parentNode', this);
157
- childNodes.splice(i, 0, node);
158
- this.setAttribute('childNodes', childNodes);
151
+ (0, debug_1.setChildNodes)(this, i, 0, [node]);
159
152
  return node;
160
153
  }
161
154
  /**
@@ -573,7 +566,6 @@ class AstElement extends node_1.AstNode {
573
566
  #getChildIndex(node) {
574
567
  const i = this.childNodes.indexOf(node);
575
568
  if (i === -1) {
576
- Parser.error('找不到子节点!', node);
577
569
  throw new RangeError('找不到子节点!');
578
570
  }
579
571
  return i;
@@ -586,7 +578,6 @@ class AstElement extends node_1.AstNode {
586
578
  this.removeAt(this.#getChildIndex(node));
587
579
  return node;
588
580
  }
589
- /** @ignore */
590
581
  insertBefore(child, reference) {
591
582
  return reference === undefined
592
583
  ? this.insertAt(child)
package/dist/lib/node.js CHANGED
@@ -205,6 +205,10 @@ class AstNode {
205
205
  return typeError(this.constructor, method, ...types);
206
206
  }
207
207
  /** @private */
208
+ constructorError(msg) {
209
+ throw new Error(`${this.constructor.name} ${msg}!`);
210
+ }
211
+ /** @private */
208
212
  seal(key) {
209
213
  this.#optional.add(key);
210
214
  Object.defineProperty(this, key, {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Ranges = exports.Range = void 0;
4
4
  const constants_1 = require("../util/constants");
5
+ const debug_1 = require("../util/debug");
5
6
  /** 模拟Python的Range对象。除`step`至少为`1`外,允许负数、小数或`end < start`的情形。 */
6
7
  class Range {
7
8
  #start;
@@ -102,7 +103,7 @@ class Ranges extends Array {
102
103
  * @param arr 参考数组
103
104
  */
104
105
  applyTo(arr) {
105
- const length = typeof arr === 'number' ? arr : arr.length, a = new Array(length).fill(undefined).map((_, i) => i);
106
+ const length = typeof arr === 'number' ? arr : arr.length, a = (0, debug_1.emptyArray)(length, i => i);
106
107
  return [
107
108
  ...new Set([...this].flatMap(ele => {
108
109
  if (typeof ele === 'number') {
package/dist/lib/text.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AstText = void 0;
4
4
  const constants_1 = require("../util/constants");
5
+ const debug_1 = require("../util/debug");
5
6
  const Parser = require("../index");
6
7
  const node_1 = require("./node");
7
8
  const errorSyntax = /<\s*\/?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*\])|(?<=^|\])([^[]*?)\]+|\]{2,}|https?[:/]\/+/giu, errorSyntaxUrl = /<\s*\/?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*\])|(?<=^|\])([^[]*?)\]+|\]{2,}/giu, disallowedTags = [
@@ -215,11 +216,9 @@ class AstText extends node_1.AstNode {
215
216
  if (!parentNode) {
216
217
  throw new Error('待分裂的文本节点没有父节点!');
217
218
  }
218
- const newText = new AstText(data.slice(offset)), childNodes = [...parentNode.childNodes];
219
+ const newText = new AstText(data.slice(offset));
220
+ (0, debug_1.setChildNodes)(parentNode, parentNode.childNodes.indexOf(this) + 1, 0, [newText]);
219
221
  this.setAttribute('data', data.slice(0, offset));
220
- childNodes.splice(childNodes.indexOf(this) + 1, 0, newText);
221
- newText.setAttribute('parentNode', parentNode);
222
- parentNode.setAttribute('childNodes', childNodes);
223
222
  return newText;
224
223
  }
225
224
  /** @private */
@@ -228,7 +227,7 @@ class AstText extends node_1.AstNode {
228
227
  return super.getRelativeIndex();
229
228
  }
230
229
  else if (j < 0 || j > this.length) {
231
- throw new RangeError(`超出文本长度范围!`);
230
+ throw new RangeError('超出文本长度范围!');
232
231
  }
233
232
  return j;
234
233
  }
@@ -64,13 +64,8 @@ const attributesParent = (constructor, i = 0) => {
64
64
  getAttrs() {
65
65
  return this.#attributesChild.getAttrs();
66
66
  }
67
- /**
68
- * 对AttributesToken子节点设置属性
69
- * @param key 属性键
70
- * @param value 属性值
71
- */
72
- setAttr(key, value) {
73
- this.#attributesChild.setAttr(key, value);
67
+ setAttr(keyOrProp, value) {
68
+ this.#attributesChild.setAttr(keyOrProp, value);
74
69
  }
75
70
  /**
76
71
  * 移除AttributesToken子节点的某属性
@@ -11,19 +11,12 @@ const fixed = (constructor) => {
11
11
  /** 不可增删子节点的类 */
12
12
  class FixedToken extends constructor {
13
13
  static fixed = true;
14
- /**
15
- * @override
16
- * @throws `Error` 不可用
17
- */
14
+ /** @override */
18
15
  removeAt() {
19
- throw new Error(`${this.constructor.name} 不可删除元素!`);
16
+ this.constructorError('不可删除元素');
20
17
  }
21
- /** @ignore */
22
18
  insertAt(token, i) {
23
- if (debug_1.Shadow.running) {
24
- return super.insertAt(token, i);
25
- }
26
- throw new Error(`${this.constructor.name} 不可插入元素!`);
19
+ return debug_1.Shadow.running ? super.insertAt(token, i) : this.constructorError('不可插入元素');
27
20
  }
28
21
  }
29
22
  return FixedToken;
@@ -4,7 +4,6 @@ exports.syntax = void 0;
4
4
  const debug_1 = require("../util/debug");
5
5
  const constants_1 = require("../util/constants");
6
6
  const string_1 = require("../util/string");
7
- const Parser = require("../index");
8
7
  /**
9
8
  * 满足特定语法格式的Token
10
9
  * @param constructor 基类
@@ -19,8 +18,7 @@ const syntax = (constructor, pattern) => {
19
18
  const /** @implements */ syntaxListener = (e, data) => {
20
19
  if (!debug_1.Shadow.running && !this.#pattern.test(this.text())) {
21
20
  (0, debug_1.undo)(e, data);
22
- Parser.error(`不可修改 ${this.constructor.name} 的语法!`, this.#pattern);
23
- throw new Error(`不可修改 ${this.constructor.name} 的语法!`);
21
+ this.constructorError('不可修改语法');
24
22
  }
25
23
  };
26
24
  this.addEventListener(['remove', 'insert', 'replace', 'text'], syntaxListener);
@@ -25,6 +25,13 @@ const parseBraces = (wikitext, config = Parser.getConfig(), accum = []) => {
25
25
  mt.index += length;
26
26
  }
27
27
  const { 0: syntax, index: curIndex } = mt ?? { 0: '\n', index: wikitext.length }, top = stack.pop() ?? {}, { 0: open, index, parts, findEqual: topFindEqual, pos: topPos } = top, innerEqual = syntax === '=' && topFindEqual;
28
+ /**
29
+ * 填入模板内容
30
+ * @param text wikitext全文
31
+ */
32
+ const push = (text) => {
33
+ parts.at(-1).push(text.slice(topPos, curIndex));
34
+ };
28
35
  if (syntax === ']]' || syntax === '}-') { // 情形1:闭合内链或转换
29
36
  lastIndex = curIndex + 2;
30
37
  }
@@ -43,7 +50,7 @@ const parseBraces = (wikitext, config = Parser.getConfig(), accum = []) => {
43
50
  }
44
51
  else if (syntax === '|' || innerEqual) { // 情形3:模板内部,含行首单个'='
45
52
  lastIndex = curIndex + 1;
46
- parts.at(-1).push(wikitext.slice(topPos, curIndex));
53
+ push(wikitext);
47
54
  if (syntax === '|') {
48
55
  parts.push([]);
49
56
  }
@@ -54,7 +61,7 @@ const parseBraces = (wikitext, config = Parser.getConfig(), accum = []) => {
54
61
  else if (syntax.startsWith('}}')) { // 情形4:闭合模板
55
62
  const close = syntax.slice(0, Math.min(open.length, 3)), rest = open.length - close.length, { length } = accum;
56
63
  lastIndex = curIndex + close.length; // 这不是最终的lastIndex
57
- parts.at(-1).push(wikitext.slice(topPos, curIndex));
64
+ push(wikitext);
58
65
  let skip = false, ch = 't';
59
66
  if (close.length === 3) {
60
67
  const argParts = parts.map(part => part.join('=')), str = argParts.length > 1 && (0, string_1.removeComment)(argParts[1]).trim();
@@ -17,27 +17,35 @@ const comment_1 = require("../src/nowiki/comment");
17
17
  */
18
18
  const parseCommentAndExt = (wikitext, config = Parser.getConfig(), accum = [], includeOnly = false) => {
19
19
  const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', { length } = onlyincludeLeft;
20
+ /** 更新`<onlyinclude>`和`</onlyinclude>`的位置 */
21
+ const update = () => {
22
+ const i = wikitext.indexOf(onlyincludeLeft);
23
+ return { i, j: wikitext.indexOf(onlyincludeRight, i + length) };
24
+ };
20
25
  if (includeOnly) {
21
- let i = wikitext.indexOf(onlyincludeLeft), j = wikitext.indexOf(onlyincludeRight, i + length);
26
+ let { i, j } = update();
22
27
  if (i !== -1 && j !== -1) { // `<onlyinclude>`拥有最高优先级
23
28
  let str = '';
29
+ /**
30
+ * 忽略未被`<onlyinclude>`和`</onlyinclude>`包裹的内容
31
+ * @param text 未被包裹的内容
32
+ */
33
+ const noinclude = (text) => {
34
+ new noinclude_1.NoincludeToken(text, config, accum);
35
+ str += `\0${accum.length - 1}c\x7F`;
36
+ };
24
37
  while (i !== -1 && j !== -1) {
25
38
  const token = `\0${accum.length}e\x7F`;
26
39
  new onlyinclude_1.OnlyincludeToken(wikitext.slice(i + length, j), config, accum);
27
40
  if (i > 0) {
28
- new noinclude_1.NoincludeToken(wikitext.slice(0, i), config, accum);
29
- str += `\0${accum.length - 1}c\x7F${token}`;
30
- }
31
- else {
32
- str += token;
41
+ noinclude(wikitext.slice(0, i));
33
42
  }
43
+ str += token;
34
44
  wikitext = wikitext.slice(j + length + 1);
35
- i = wikitext.indexOf(onlyincludeLeft);
36
- j = wikitext.indexOf(onlyincludeRight, i + length);
45
+ ({ i, j } = update());
37
46
  }
38
47
  if (wikitext) {
39
- new noinclude_1.NoincludeToken(wikitext, config, accum);
40
- str += `\0${accum.length - 1}c\x7F`;
48
+ noinclude(wikitext);
41
49
  }
42
50
  return str;
43
51
  }
@@ -23,17 +23,24 @@ const parseList = (wikitext, config = Parser.getConfig(), accum = []) => {
23
23
  return text;
24
24
  }
25
25
  let regex = /:+|-\{/gu, ex = regex.exec(text), lc = 0;
26
+ /**
27
+ * 创建`DdToken`
28
+ * @param syntax `:`
29
+ * @param index 起点
30
+ */
31
+ const dd = (syntax, index) => {
32
+ new dd_1.DdToken(syntax, config, accum);
33
+ return `${text.slice(0, index)}\0${accum.length - 1}d\x7F${text.slice(index + syntax.length)}`;
34
+ };
26
35
  while (ex && dt) {
27
36
  const { 0: syntax, index } = ex;
28
37
  if (syntax.startsWith(':')) {
29
38
  if (syntax.length >= dt) {
30
- new dd_1.DdToken(':'.repeat(dt), config, accum);
31
- return `${text.slice(0, index)}\0${accum.length - 1}d\x7F${text.slice(index + dt)}`;
39
+ return dd(syntax.slice(0, dt), index);
32
40
  }
33
- text = `${text.slice(0, index)}\0${accum.length}d\x7F${text.slice(regex.lastIndex)}`;
34
41
  dt -= syntax.length;
35
42
  regex.lastIndex = index + 4 + String(accum.length).length;
36
- new dd_1.DdToken(syntax, config, accum);
43
+ text = dd(syntax, index);
37
44
  }
38
45
  else if (syntax === '-{') {
39
46
  if (!lc) {
@@ -72,21 +72,6 @@ const desanitize = (selector) => {
72
72
  * @param val 属性值或伪选择器函数的参数
73
73
  */
74
74
  const deQuote = (val) => /^(["']).*\1$/u.test(val) ? val.slice(1, -1) : val.trim();
75
- /**
76
- * 解析简单伪选择器
77
- * @param step 当前顶部
78
- * @param str 不含属性和复杂伪选择器的语句
79
- * @throws `SyntaxError` 非法的选择器
80
- */
81
- const pushSimple = (step, str) => {
82
- const pieces = str.trim().split(':'),
83
- // eslint-disable-next-line unicorn/explicit-length-check
84
- i = pieces.slice(1).findIndex(pseudo => simplePseudos.has(pseudo)) + 1 || pieces.length;
85
- if (pieces.slice(i).some(pseudo => !simplePseudos.has(pseudo))) {
86
- throw new SyntaxError(`非法的选择器!\n${str}\n可能需要将':'转义为'\\:'。`);
87
- }
88
- step.push(desanitize(pieces.slice(0, i).join(':')), ...pieces.slice(i).map(piece => `:${piece}`));
89
- };
90
75
  /**
91
76
  * 解析选择器
92
77
  * @param selector
@@ -96,6 +81,20 @@ const parseSelector = (selector) => {
96
81
  selector = selector.trim();
97
82
  const stack = [[[]]];
98
83
  let sanitized = sanitize(selector), regex = regularRegex, mt = regex.exec(sanitized), [condition] = stack, [step] = condition;
84
+ /**
85
+ * 解析简单伪选择器
86
+ * @param index 伪选择器的终点位置
87
+ * @throws `SyntaxError` 非法的选择器
88
+ */
89
+ const pushSimple = (index) => {
90
+ const str = sanitized.slice(0, index), pieces = str.trim().split(':'),
91
+ // eslint-disable-next-line unicorn/explicit-length-check
92
+ i = pieces.slice(1).findIndex(pseudo => simplePseudos.has(pseudo)) + 1 || pieces.length;
93
+ if (pieces.slice(i).some(pseudo => !simplePseudos.has(pseudo))) {
94
+ throw new SyntaxError(`非法的选择器!\n${str}\n可能需要将':'转义为'\\:'。`);
95
+ }
96
+ step.push(desanitize(pieces.slice(0, i).join(':')), ...pieces.slice(i).map(piece => `:${piece}`));
97
+ };
99
98
  while (mt) {
100
99
  let { 0: syntax, index } = mt;
101
100
  if (syntax.trim() === '') {
@@ -104,13 +103,13 @@ const parseSelector = (selector) => {
104
103
  syntax = grouping.has(char) ? char : '';
105
104
  }
106
105
  if (syntax === ',') { // 情形1:并列
107
- pushSimple(step, sanitized.slice(0, index));
106
+ pushSimple(index);
108
107
  condition = [[]];
109
108
  [step] = condition;
110
109
  stack.push(condition);
111
110
  }
112
111
  else if (combinator.has(syntax)) { // 情形2:关系
113
- pushSimple(step, sanitized.slice(0, index));
112
+ pushSimple(index);
114
113
  if (!step.some(Boolean)) {
115
114
  throw new SyntaxError(`非法的选择器!\n${selector}\n可能需要通用选择器'*'。`);
116
115
  }
@@ -119,7 +118,7 @@ const parseSelector = (selector) => {
119
118
  condition.push(step);
120
119
  }
121
120
  else if (syntax === '[') { // 情形3:属性开启
122
- pushSimple(step, sanitized.slice(0, index));
121
+ pushSimple(index);
123
122
  regex = attributeRegex;
124
123
  }
125
124
  else if (syntax.endsWith(']')) { // 情形4:属性闭合
@@ -132,7 +131,7 @@ const parseSelector = (selector) => {
132
131
  if (!pseudoExec) {
133
132
  throw new SyntaxError(`非法的选择器!\n${desanitize(sanitized)}\n请检查伪选择器是否存在。`);
134
133
  }
135
- pushSimple(step, sanitized.slice(0, pseudoExec.index));
134
+ pushSimple(pseudoExec.index);
136
135
  step.push(pseudoExec[1]); // 临时存放复杂伪选择器
137
136
  regex = functionRegex;
138
137
  }
@@ -149,7 +148,7 @@ const parseSelector = (selector) => {
149
148
  mt = regex.exec(sanitized);
150
149
  }
151
150
  if (regex === regularRegex) {
152
- pushSimple(step, sanitized);
151
+ pushSimple(Infinity);
153
152
  const pseudos = new Set(stack.flat(2).filter((e) => typeof e === 'string' && e.startsWith(':')));
154
153
  if (pseudos.size > 0) {
155
154
  Parser.warn('检测到伪选择器,请确认是否需要将":"转义成"\\:"。', pseudos);
@@ -8,7 +8,10 @@ const index_2 = require("../src/table/index");
8
8
  const tr_1 = require("../src/table/tr");
9
9
  const td_1 = require("../src/table/td");
10
10
  const dd_1 = require("../src/nowiki/dd");
11
- /** @ignore */
11
+ /**
12
+ * 判断是否为表格行或表格
13
+ * @param token 表格节点
14
+ */
12
15
  const isTr = (token) => token.lastChild.constructor !== index_1.Token;
13
16
  /**
14
17
  * 解析表格,注意`tr`和`td`包含开头的换行
@@ -20,30 +23,32 @@ const parseTable = ({ firstChild: { data }, type, name }, config = Parser.getCon
20
23
  const stack = [], lines = data.split('\n');
21
24
  let out = type === 'root' || type === 'parameter-value' || type === 'ext-inner' && name === 'poem'
22
25
  ? ''
23
- : `\n${lines.shift()}`;
26
+ : `\n${lines.shift()}`, top;
24
27
  /**
25
28
  * 向表格中插入纯文本
26
29
  * @param str 待插入的文本
27
- * @param top 当前解析的表格或表格行
30
+ * @param topToken 当前解析的表格或表格行
28
31
  */
29
- const push = (str, top) => {
30
- if (!top) {
32
+ const push = (str, topToken) => {
33
+ if (!topToken) {
31
34
  out += str;
32
35
  return;
33
36
  }
34
- const { lastChild } = top;
35
- if (isTr(top)) {
37
+ const { lastChild } = topToken;
38
+ if (isTr(topToken)) {
36
39
  const token = new index_1.Token(str, config, accum);
37
40
  token.type = 'table-inter';
38
41
  token.setAttribute('stage', 3);
39
- top.insertAt(token);
42
+ topToken.insertAt(token);
40
43
  }
41
44
  else {
42
45
  lastChild.setText(String(lastChild) + str);
43
46
  }
44
- };
47
+ },
48
+ /** 取出最近的表格行 */
49
+ pop = () => top.type === 'td' ? stack.pop() : top;
45
50
  for (const outLine of lines) {
46
- let top = stack.pop();
51
+ top = stack.pop();
47
52
  const [spaces] = /^(?:\s|\0\d+c\x7F)*/u.exec(outLine), line = outLine.slice(spaces.length), matchesStart = /^(:*)((?:\s|\0\d+c\x7F)*)(\{\||\{(?:\0\d+c\x7F)*\0\d+!\x7F|\0\d+\{\x7F)(.*)$/u
48
53
  .exec(line);
49
54
  if (matchesStart) {
@@ -79,9 +84,7 @@ const parseTable = ({ firstChild: { data }, type, name }, config = Parser.getCon
79
84
  push(attr, stack.at(-1));
80
85
  }
81
86
  else if (row) {
82
- if (top.type === 'td') {
83
- top = stack.pop();
84
- }
87
+ top = pop();
85
88
  if (top.type === 'tr') {
86
89
  top = stack.pop();
87
90
  }
@@ -90,20 +93,25 @@ const parseTable = ({ firstChild: { data }, type, name }, config = Parser.getCon
90
93
  top.insertAt(tr);
91
94
  }
92
95
  else {
93
- if (top.type === 'td') {
94
- top = stack.pop();
95
- }
96
+ top = pop();
96
97
  const regex = cell === '!' ? /!!|(?:\||\0\d+!\x7F){2}|\0\d+\+\x7F/gu : /(?:\||\0\d+!\x7F){2}|\0\d+\+\x7F/gu;
97
98
  let mt = regex.exec(attr), lastIndex = 0, lastSyntax = `\n${spaces}${cell}`;
99
+ /**
100
+ * 创建表格单元格
101
+ * @param tr 当前表格行
102
+ */
103
+ const createTd = (tr) => {
104
+ const td = new td_1.TdToken(lastSyntax, attr.slice(lastIndex, mt?.index), config, accum);
105
+ tr.insertAt(td);
106
+ return td;
107
+ };
98
108
  while (mt) {
99
- top.insertAt(new td_1.TdToken(lastSyntax, attr.slice(lastIndex, mt.index), config, accum));
109
+ createTd(top);
100
110
  ({ lastIndex } = regex);
101
111
  [lastSyntax] = mt;
102
112
  mt = regex.exec(attr);
103
113
  }
104
- const td = new td_1.TdToken(lastSyntax, attr.slice(lastIndex), config, accum);
105
- stack.push(top, td);
106
- top.insertAt(td);
114
+ stack.push(top, createTd(top));
107
115
  }
108
116
  }
109
117
  return out.slice(1);
package/dist/src/arg.d.ts CHANGED
@@ -41,8 +41,6 @@ export declare class ArgToken extends Token {
41
41
  * @override
42
42
  * @param token 待插入的子节点
43
43
  * @param i 插入位置
44
- * @throws `RangeError` 不可插入多余子节点
45
- * @throws `TypeError` 不可插入文本节点
46
44
  */
47
45
  insertAt<T extends Token>(token: T, i?: number): T;
48
46
  /**
package/dist/src/arg.js CHANGED
@@ -141,16 +141,14 @@ class ArgToken extends index_1.Token {
141
141
  * @override
142
142
  * @param token 待插入的子节点
143
143
  * @param i 插入位置
144
- * @throws `RangeError` 不可插入多余子节点
145
- * @throws `TypeError` 不可插入文本节点
146
144
  */
147
145
  insertAt(token, i = this.length) {
148
146
  i += i < 0 ? this.length : 0;
149
147
  if (i > 1) {
150
- throw new RangeError(`${this.constructor.name}不可插入多余的子节点!`);
148
+ this.constructorError('不可插入多余的子节点');
151
149
  }
152
150
  else if (typeof token === 'string') {
153
- throw new TypeError(`${this.constructor.name}不可插入文本节点!`);
151
+ this.constructorError('不可插入文本节点');
154
152
  }
155
153
  super.insertAt(token, i);
156
154
  if (i === 1) {
@@ -17,6 +17,7 @@ declare const AttributeToken_base: ((abstract new (...args: any[]) => {
17
17
  setAttribute<T_2 extends string>(key: T_2, value: TokenAttributeSetter<T_2>): void;
18
18
  addEventListener(events: string | string[], listener: AstListener): void;
19
19
  replaceChildren(...elements: (string | Parser.AstNodes)[]): void;
20
+ constructorError(msg: string): never;
20
21
  }) & {
21
22
  readonly fixed: true;
22
23
  }) & typeof Parser.Token;
@@ -360,15 +360,15 @@ class AttributeToken extends (0, fixed_1.fixed)(index_1.Token) {
360
360
  else if (this.type === 'ext-attr' && value.includes('>')) {
361
361
  throw new RangeError('扩展标签属性不能包含 ">"!');
362
362
  }
363
- else if (value.includes('"') && value.includes("'")) {
363
+ else if (value.includes('"') && value.includes(`'`)) {
364
364
  throw new RangeError('属性值不能同时包含单引号和双引号!');
365
365
  }
366
366
  const config = this.getAttribute('config'), { childNodes } = Parser.parse(value, this.getAttribute('include'), stages[this.type] + 1, config);
367
367
  this.lastChild.replaceChildren(...childNodes);
368
368
  if (value.includes('"')) {
369
- this.#quotes = ["'", "'"];
369
+ this.#quotes = [`'`, `'`];
370
370
  }
371
- else if (value.includes("'") || !this.#quotes[0]) {
371
+ else if (value.includes(`'`) || !this.#quotes[0]) {
372
372
  this.#quotes = ['"', '"'];
373
373
  }
374
374
  else {
@@ -70,16 +70,18 @@ export declare class AttributesToken extends Token {
70
70
  * @override
71
71
  * @param token 待插入的子节点
72
72
  * @param i 插入位置
73
- * @throws `RangeError` 不是AttributeToken或标签不匹配
73
+ * @throws `RangeError` 标签不匹配
74
74
  */
75
75
  insertAt<T extends AttributeToken | AtomToken>(token: T, i?: number): T;
76
76
  /**
77
77
  * 设置指定属性
78
78
  * @param key 属性键
79
79
  * @param value 属性值
80
+ * @param prop 属性对象
80
81
  * @throws `RangeError` 扩展标签属性不能包含">"
81
82
  */
82
83
  setAttr(key: string, value: string | boolean): void;
84
+ setAttr(prop: Record<string, string | boolean>): void;
83
85
  /**
84
86
  * 标签是否具有某属性
85
87
  * @param key 属性键