wikiparser-node 0.0.2 → 0.0.3

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.
package/README.md CHANGED
@@ -29,6 +29,7 @@
29
29
  10. [findEnclosingHtml](#token.findenclosinghtml)
30
30
  11. [getCategories](#token.getcategories)
31
31
  12. [redoQuotes](#token.redoquotes)
32
+ 13. [print](#token.print)
32
33
  2. [实例属性](#token.instance.properties)
33
34
  1. [type](#token.type)
34
35
  3. [原型属性](#token.prototype.properties)
@@ -426,6 +427,15 @@ assert.deepStrictEqual(root.childNodes, ["'", root.firstElementChild, "a", root.
426
427
  var root = Parser.parse(wikitext);
427
428
  assert(root.type === 'root');
428
429
  ```
430
+
431
+ **print**(format?: 'markup'\|'json' = 'markup'): void\|object<a id="token.print"></a>
432
+ - 打印解析生成的 AST。
433
+
434
+ ```js
435
+ var root = Parser.parse("<ref>{{T|<br>\n----\n[[File:F|thumb|''[//example.net]'']]}}</ref>");
436
+ root.print();
437
+ root.print('json', 'example'); // JSON格式的输出结果将保存至 /printed/example.json 文件
438
+ ```
429
439
  </details>
430
440
 
431
441
  ## 原型属性<a id="token.prototype.properties"></a>
@@ -930,7 +940,7 @@ param.setValue(' 2 ');
930
940
  assert(root.toString() === '{{a|b= 2 }}'); // setValue方法总是保留空白字符,哪怕是无效的
931
941
  ```
932
942
 
933
- **rename**(key: string, force: boolean): void<a id="parametertoken.rename"></a>
943
+ **rename**(key: string, force?: boolean = false): void<a id="parametertoken.rename"></a>
934
944
  - 重命名参数,可选是否在导致重复参数时抛出错误。
935
945
 
936
946
  ```js
@@ -1164,7 +1174,7 @@ assert(root.toString() === '{|\n!colspan=2|\n|-\n| \n!\n|}');
1164
1174
  |:-:|:-:|:-:|
1165
1175
  |<table><tr><td colspan=2>td</td></tr><tr><td>td</td><td>td</td></tr></table>|<table><tr><td colspan=2>td</td></tr><tr><td>td</td><th>th</th></tr></table>|<table><tr><th colspan=2>th</th></tr><tr><td>td</td><th>th</th></tr></table>|
1166
1176
 
1167
- **insertTableRow**(row: number, attr: Record\<string, string\|boolean>, inner?: string, subtype?: 'td'\|'th', innerAttr?: Record\<string, string\|boolean>): TrToken<a id="tabletoken.inserttablerow"></a>
1177
+ **insertTableRow**(row: number, attr: Record\<string, string\|boolean>, inner?: string, subtype?: 'td'\|'th' = 'td', innerAttr?: Record\<string, string\|boolean>): TrToken<a id="tabletoken.inserttablerow"></a>
1168
1178
  - 插入空行或一行单元格。
1169
1179
 
1170
1180
  ```js
@@ -1179,7 +1189,7 @@ assert(root.toString() === '{|\n|a|| rowspan="3"|b||c\n|- class="tr"\n|-\n! clas
1179
1189
  |:-:|:-:|
1180
1190
  |<table><tr><td>a</td><td rowspan=2>b</td><td>c</td></tr><tr><td>d</td><td>e</td></tr></table>|<table><tr><td>a</td><td rowspan=3>b</td><td>c</td></tr><tr><th>f</th><th>f</th></tr><tr><td>d</td><td>e</td></tr></table>|
1181
1191
 
1182
- **insertTableCol**(x: number, inner: string, subtype: 'td'\|'th', attr: Record\<string, string\|boolean>): void<a id="tabletoken.inserttablecol"></a>
1192
+ **insertTableCol**(x: number, inner: string, subtype?: 'td'\|'th' = 'td', attr?: Record\<string, string\|boolean>): void<a id="tabletoken.inserttablecol"></a>
1183
1193
  - 插入一列单元格。
1184
1194
 
1185
1195
  ```js
@@ -1193,7 +1203,7 @@ assert(root.toString() === '{|\n| colspan="3"|a\n|-\n|b\n! class="th"|d\n|c\n|}'
1193
1203
  |:-:|:-:|
1194
1204
  |<table><tr><td colspan=2 align="center">a</td></tr><tr><td>b</td><td>c</td></tr></table>|<table><tr><td colspan=3 align="center">a</td></tr><tr><td>b</td><th>d</th><td>c</td></tr></table>|
1195
1205
 
1196
- **insertTableCell**(inner: string, coords: {row: number, column: number}\|{x: number, y: number}, subtype: 'td'\|'th', attr: Record\<string, string\|boolean>): [TdToken](#tdtoken)<a id="tabletoken.inserttablecell"></a>
1206
+ **insertTableCell**(inner: string, coords: {row: number, column: number}\|{x: number, y: number}, subtype?: 'td'\|'th' = 'td', attr?: Record\<string, string\|boolean>): [TdToken](#tdtoken)<a id="tabletoken.inserttablecell"></a>
1197
1207
  - 插入一个单元格。
1198
1208
 
1199
1209
  ```js
@@ -1471,7 +1481,7 @@ link.asSelfLink();
1471
1481
  assert(root.toString() === '[[#b]]');
1472
1482
  ```
1473
1483
 
1474
- **setLinkText**(linkText?: string)<a id="linktoken.setlinktext"></a>
1484
+ **setLinkText**(linkText: string)<a id="linktoken.setlinktext"></a>
1475
1485
  - 修改链接文本。
1476
1486
 
1477
1487
  ```js
package/errors/README ADDED
@@ -0,0 +1 @@
1
+ 这里记录解析失败时处于半解析状态的维基文本以及错误信息。
package/index.js CHANGED
@@ -108,7 +108,7 @@ const /** @type {Parser} */ Parser = {
108
108
  }
109
109
  }
110
110
  };
111
- Parser.run(() => {
111
+ this.run(() => {
112
112
  build(['title', 'fragment']);
113
113
  });
114
114
  }
@@ -119,25 +119,58 @@ const /** @type {Parser} */ Parser = {
119
119
 
120
120
  parse(wikitext, include = false, maxStage = Parser.MAX_STAGE, config = Parser.getConfig()) {
121
121
  const Token = require('./src');
122
+ let token;
122
123
  this.run(() => {
123
124
  if (typeof wikitext === 'string') {
124
- wikitext = new Token(wikitext, config);
125
- } else if (!(wikitext instanceof Token)) {
125
+ token = new Token(wikitext, config);
126
+ } else if (wikitext instanceof Token) {
127
+ token = wikitext;
128
+ wikitext = token.toString();
129
+ } else {
126
130
  throw new TypeError('待解析的内容应为 String 或 Token!');
127
131
  }
128
132
  try {
129
- wikitext.parse(maxStage, include);
133
+ token.parse(maxStage, include);
130
134
  } catch (e) {
131
135
  if (e instanceof Error) {
132
- fs.writeFileSync(
133
- `${__dirname}/errors/${new Date().toISOString()}`,
134
- `${e.stack}\n\n\n${wikitext.toString()}`,
135
- );
136
+ const file = `${__dirname}/errors/${new Date().toISOString()}`,
137
+ stage = token.getAttribute('stage');
138
+ fs.writeFileSync(file, stage === this.MAX_STAGE ? wikitext : token.toString());
139
+ fs.writeFileSync(`${file}.err`, e.stack);
140
+ fs.writeFileSync(`${file}.json`, JSON.stringify({
141
+ stage, include: token.getAttribute('include'), config: this.config,
142
+ }, null, '\t'));
136
143
  }
137
144
  throw e;
138
145
  }
139
146
  });
140
- return wikitext;
147
+ return token;
148
+ },
149
+
150
+ reparse(date) {
151
+ const path = `${__dirname}/errors/`,
152
+ main = fs.readdirSync(path).find(name => name.startsWith(date) && name.endsWith('Z'));
153
+ if (!main) {
154
+ throw new RangeError(`找不到对应时间戳的错误记录:${date}`);
155
+ }
156
+ const Token = require('./src'),
157
+ file = `${path}${main}`,
158
+ wikitext = fs.readFileSync(file, 'utf8'),
159
+ {stage, include, config} = require(`${file}.json`);
160
+ this.config = config;
161
+ return this.run(() => {
162
+ const halfParsed = stage < this.MAX_STAGE,
163
+ token = new Token(wikitext, this.getConfig(), halfParsed);
164
+ if (halfParsed) {
165
+ token.setAttribute('stage', stage).parseOnce(stage, include);
166
+ } else {
167
+ token.parse(undefined, include);
168
+ }
169
+ fs.unlinkSync(file);
170
+ fs.unlinkSync(`${file}.err`);
171
+ fs.unlinkSync(`${file}.json`);
172
+ return token;
173
+ });
141
174
  },
142
175
 
143
176
  getTool() {
package/lib/element.js CHANGED
@@ -1,7 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const {typeError, externalUse} = require('../util/debug'),
4
- {toCase} = require('../util/string'),
3
+ const fs = require('fs'),
4
+ {externalUse} = require('../util/debug'),
5
+ {toCase, noWrap} = require('../util/string'),
5
6
  {nth} = require('./ranges'),
6
7
  EventEmitter = require('events'),
7
8
  AstNode = require('./node'),
@@ -93,7 +94,7 @@ class AstElement extends AstNode {
93
94
  this.addEventListener(type, listener, options);
94
95
  }
95
96
  } else if (typeof types !== 'string' || typeof listener !== 'function') {
96
- typeError(this, 'addEventListener', 'String', 'Function');
97
+ this.typeError('addEventListener', 'String', 'Function');
97
98
  } else {
98
99
  this.#events[options?.once ? 'once' : 'on'](types, listener);
99
100
  }
@@ -109,7 +110,7 @@ class AstElement extends AstNode {
109
110
  this.removeEventListener(type, listener);
110
111
  }
111
112
  } else if (typeof types !== 'string' || typeof listener !== 'function') {
112
- typeError(this, 'removeEventListener', 'String', 'Function');
113
+ this.typeError('removeEventListener', 'String', 'Function');
113
114
  } else {
114
115
  this.#events.off(types, listener);
115
116
  }
@@ -122,7 +123,7 @@ class AstElement extends AstNode {
122
123
  this.removeAllEventListeners(type);
123
124
  }
124
125
  } else if (types !== undefined && typeof types !== 'string') {
125
- typeError(this, 'removeAllEventListeners', 'String');
126
+ this.typeError('removeAllEventListeners', 'String');
126
127
  } else {
127
128
  this.#events.removeAllListeners(types);
128
129
  }
@@ -134,7 +135,7 @@ class AstElement extends AstNode {
134
135
  */
135
136
  listEventListeners(type) {
136
137
  if (typeof type !== 'string') {
137
- typeError(this, 'listEventListeners', 'String');
138
+ this.typeError('listEventListeners', 'String');
138
139
  }
139
140
  return this.#events.listeners(type);
140
141
  }
@@ -145,7 +146,7 @@ class AstElement extends AstNode {
145
146
  */
146
147
  dispatchEvent(e, data) {
147
148
  if (!(e instanceof Event)) {
148
- typeError(this, 'dispatchEvent', 'Event');
149
+ this.typeError('dispatchEvent', 'Event');
149
150
  } else if (!e.target) { // 初始化
150
151
  Object.defineProperty(e, 'target', {value: this, enumerable: true});
151
152
  e.stopPropagation = function() {
@@ -357,7 +358,7 @@ class AstElement extends AstNode {
357
358
  */
358
359
  matches(selector = '', simple = false) {
359
360
  if (typeof selector !== 'string') {
360
- typeError(this, 'matches', 'String');
361
+ this.typeError('matches', 'String');
361
362
  } else if (!selector.trim()) {
362
363
  return true;
363
364
  }
@@ -479,7 +480,7 @@ class AstElement extends AstNode {
479
480
  */
480
481
  comparePosition(other) {
481
482
  if (!(other instanceof AstElement)) {
482
- typeError(this, 'comparePosition', 'AstElement');
483
+ this.typeError('comparePosition', 'AstElement');
483
484
  } else if (this === other) {
484
485
  return 0;
485
486
  } else if (this.contains(other)) {
@@ -541,7 +542,7 @@ class AstElement extends AstNode {
541
542
  */
542
543
  posFromIndex(index) {
543
544
  if (typeof index !== 'number') {
544
- typeError(this, 'posFromIndex', 'Number');
545
+ this.typeError('posFromIndex', 'Number');
545
546
  }
546
547
  const text = this.toString();
547
548
  if (index < -text.length || index >= text.length || !Number.isInteger(index)) {
@@ -558,7 +559,7 @@ class AstElement extends AstNode {
558
559
  */
559
560
  indexFromPos(top, left) {
560
561
  if (typeof top !== 'number' || typeof left !== 'number') {
561
- typeError(this, 'indexFromPos', 'Number');
562
+ this.typeError('indexFromPos', 'Number');
562
563
  } else if (top < 0 || left < 0 || !Number.isInteger(top) || !Number.isInteger(left)) {
563
564
  return;
564
565
  }
@@ -582,7 +583,7 @@ class AstElement extends AstNode {
582
583
  */
583
584
  getRelativeIndex(j) {
584
585
  if (j !== undefined && typeof j !== 'number') {
585
- typeError(this, 'getRelativeIndex', 'Number');
586
+ this.typeError('getRelativeIndex', 'Number');
586
587
  }
587
588
  let /** @type {(string|this)[]} */ childNodes;
588
589
  /**
@@ -684,6 +685,63 @@ class AstElement extends AstNode {
684
685
  return node ? [[index + this.getRelativeIndex(i), node]] : [];
685
686
  });
686
687
  }
688
+
689
+ /**
690
+ * @template {'markup'|'json'} T
691
+ * @param {T} format
692
+ * @param {T extends 'markup' ? number : string} depth
693
+ * @returns {T extends 'markup' ? void : Record<string, any>}
694
+ */
695
+ print(format = 'markup', depth = 0) {
696
+ if (format === 'json') {
697
+ const {childNodes, ...prop} = this,
698
+ json = {
699
+ ...prop,
700
+ childNodes: childNodes.map(child => typeof child === 'string' ? child : child.print('json')),
701
+ };
702
+ if (typeof depth === 'string') {
703
+ fs.writeFileSync(
704
+ `${__dirname.slice(0, -3)}printed/${depth}${depth.endsWith('.json') ? '' : '.json'}`,
705
+ JSON.stringify(json, null, 2),
706
+ );
707
+ }
708
+ return json;
709
+ } else if (typeof depth !== 'number') {
710
+ this.typeError('print', 'Number');
711
+ }
712
+ const indent = ' '.repeat(depth),
713
+ str = this.toString(),
714
+ {childNodes, type, firstChild} = this,
715
+ {length} = childNodes;
716
+ if (!str || length === 0 || typeof firstChild === 'string' && firstChild === str) {
717
+ console.log(`${indent}\x1b[32m<%s>\x1b[0m${noWrap(str)}\x1b[32m</%s>\x1b[0m`, type, type);
718
+ return;
719
+ }
720
+ Parser.info(`${indent}<${type}>`);
721
+ let i = this.getPadding();
722
+ if (i) {
723
+ console.log(`${indent} ${noWrap(str.slice(0, i))}`);
724
+ }
725
+ for (const [j, child] of childNodes.entries()) {
726
+ const childStr = String(child),
727
+ gap = j === length - 1 ? 0 : this.getGaps(j);
728
+ if (!childStr) {
729
+ // pass
730
+ } else if (typeof child === 'string') {
731
+ console.log(`${indent} ${noWrap(child)}`);
732
+ } else {
733
+ child.print('markup', depth + 1);
734
+ }
735
+ i += childStr.length + gap;
736
+ if (gap) {
737
+ console.log(`${indent} ${noWrap(str.slice(i - gap, i))}`);
738
+ }
739
+ }
740
+ if (i < str.length) {
741
+ console.log(`${indent} ${noWrap(str.slice(i))}`);
742
+ }
743
+ Parser.info(`${indent}</${type}>`);
744
+ }
687
745
  }
688
746
 
689
747
  Parser.classes.AstElement = __filename;
package/lib/node.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const {typeError, externalUse} = require('../util/debug'),
4
- {text} = require('../util/string'),
4
+ {text, noWrap} = require('../util/string'),
5
5
  assert = require('assert/strict'),
6
6
  /** @type {Parser} */ Parser = require('..');
7
7
 
@@ -34,6 +34,14 @@ class AstNode {
34
34
  throw new Error(`${this.constructor.name}.${method} 方法仅用于代码调试!`);
35
35
  }
36
36
 
37
+ /**
38
+ * @param {string} method
39
+ * @param {...string} types
40
+ */
41
+ typeError(method, ...types) {
42
+ return typeError(this.constructor, method, ...types);
43
+ }
44
+
37
45
  /** @param {string|string[]} keys */
38
46
  seal(keys) {
39
47
  if (!Parser.running && !Parser.debugging) {
@@ -68,7 +76,7 @@ class AstNode {
68
76
  /** @param {PropertyKey} key */
69
77
  hasAttribute(key) {
70
78
  if (!['string', 'number', 'symbol'].includes(typeof key)) {
71
- typeError(this, 'hasAttribute', 'String', 'Number', 'Symbol');
79
+ this.typeError('hasAttribute', 'String', 'Number', 'Symbol');
72
80
  }
73
81
  return key in this;
74
82
  }
@@ -142,7 +150,7 @@ class AstNode {
142
150
  */
143
151
  toggleAttribute(key, force) {
144
152
  if (force !== undefined && typeof force !== 'boolean') {
145
- typeError(this, 'toggleAttribute', 'Boolean');
153
+ this.typeError('toggleAttribute', 'Boolean');
146
154
  } else if (this.hasAttribute(key) && typeof this[key] !== 'boolean') {
147
155
  throw new RangeError(`${key} 属性的值不为 Boolean!`);
148
156
  }
@@ -175,7 +183,7 @@ class AstNode {
175
183
  */
176
184
  contains(node) {
177
185
  if (!(node instanceof AstNode)) {
178
- typeError(this, 'contains', 'Token');
186
+ this.typeError('contains', 'Token');
179
187
  }
180
188
  return node === this || this.childNodes.some(child => child instanceof AstNode && child.contains(node));
181
189
  }
@@ -185,7 +193,7 @@ class AstNode {
185
193
  if (!Parser.debugging && externalUse('verifyChild')) {
186
194
  this.debugOnly('verifyChild');
187
195
  } else if (typeof i !== 'number') {
188
- typeError(this, 'verifyChild', 'Number');
196
+ this.typeError('verifyChild', 'Number');
189
197
  }
190
198
  const {length} = this.childNodes;
191
199
  if (i < -length || i >= length + addition || !Number.isInteger(i)) {
@@ -216,7 +224,7 @@ class AstNode {
216
224
  Parser.error('找不到子节点!', node);
217
225
  throw new RangeError('找不到子节点!');
218
226
  } else if (typeof node === 'string' && childNodes.lastIndexOf(node) > i) {
219
- throw new RangeError(`重复的纯文本节点 ${node.replaceAll('\n', '\\n')}!`);
227
+ throw new RangeError(`重复的纯文本节点 ${noWrap(node)}!`);
220
228
  }
221
229
  return i;
222
230
  }
@@ -238,7 +246,7 @@ class AstNode {
238
246
  */
239
247
  insertAt(node, i = this.childNodes.length) {
240
248
  if (typeof node !== 'string' && !(node instanceof AstNode)) {
241
- typeError(this, 'insertAt', 'String', 'Token');
249
+ this.typeError('insertAt', 'String', 'Token');
242
250
  } else if (node instanceof AstNode && node.contains(this)) {
243
251
  Parser.error('不能插入祖先节点!', node);
244
252
  throw new RangeError('不能插入祖先节点!');
@@ -297,7 +305,7 @@ class AstNode {
297
305
  /** @param {string} str */
298
306
  setText(str, i = 0) {
299
307
  if (typeof str !== 'string') {
300
- typeError(this, 'setText', 'String');
308
+ this.typeError('setText', 'String');
301
309
  }
302
310
  this.verifyChild(i);
303
311
  const oldText = this.childNodes.at(i);
@@ -316,7 +324,7 @@ class AstNode {
316
324
  */
317
325
  splitText(i, offset) {
318
326
  if (typeof offset !== 'number') {
319
- typeError(this, 'splitText', 'Number');
327
+ this.typeError('splitText', 'Number');
320
328
  }
321
329
  this.verifyChild(i);
322
330
  const oldText = this.childNodes.at(i);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikiparser-node",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "A Node.js parser for MediaWiki markup with AST",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -10,7 +10,7 @@ const {extUrlChar} = require('../util/string'),
10
10
  const parseExternalLinks = (firstChild, config = Parser.getConfig(), accum = []) => {
11
11
  const ExtLinkToken = require('../src/extLink'),
12
12
  regex = new RegExp(
13
- `\\[((?:${config.protocol}|//)${extUrlChar})(\\p{Zs}*)([^\\]\\x01-\\x08\\x0a-\\x1f\\ufffd]*)\\]`,
13
+ `\\[((?:${config.protocol}|//)${extUrlChar})(\\p{Zs}*)([^\\]\x01-\x08\x0a-\x1f\ufffd]*)\\]`,
14
14
  'gui',
15
15
  );
16
16
  return firstChild.replace(regex, /** @type {function(...string): string} */ (_, url, space, text) => {
package/parser/table.js CHANGED
@@ -54,10 +54,10 @@ const parseTable = ({firstChild, type}, config = Parser.getConfig(), accum = [])
54
54
  }
55
55
  const [, closing, row, cell, attr] = matches;
56
56
  if (closing) {
57
- while (top.type !== 'table') {
57
+ while (!(top instanceof TableToken)) {
58
58
  top = stack.pop();
59
59
  }
60
- top.close(`\n${spaces}${closing}`);
60
+ top.close(`\n${spaces}${closing}`, true);
61
61
  push(attr, stack.at(-1));
62
62
  } else if (row) {
63
63
  if (top.type === 'td') {
package/printed/README ADDED
@@ -0,0 +1 @@
1
+ 这里存放以 JSON 格式打印的 AST。
package/src/arg.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const {text} = require('../util/string'),
3
+ const {text, noWrap} = require('../util/string'),
4
4
  /** @type {Parser} */ Parser = require('..'),
5
5
  Token = require('.');
6
6
 
@@ -118,7 +118,7 @@ class ArgToken extends Token {
118
118
  const root = Parser.parse(`{{{${name}}}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
119
119
  {childNodes: {length}, firstElementChild} = root;
120
120
  if (length !== 1 || firstElementChild?.type !== 'arg' || firstElementChild.childElementCount !== 1) {
121
- throw new SyntaxError(`非法的参数名称:${name.replaceAll('\n', '\\n')}`);
121
+ throw new SyntaxError(`非法的参数名称:${noWrap(name)}`);
122
122
  }
123
123
  const newName = firstElementChild.firstElementChild;
124
124
  root.destroy();
@@ -132,7 +132,7 @@ class ArgToken extends Token {
132
132
  const root = Parser.parse(`{{{|${value}}}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
133
133
  {childNodes: {length}, firstElementChild} = root;
134
134
  if (length !== 1 || firstElementChild?.type !== 'arg' || firstElementChild.childElementCount !== 2) {
135
- throw new SyntaxError(`非法的参数预设值:${value.replaceAll('\n', '\\n')}`);
135
+ throw new SyntaxError(`非法的参数预设值:${noWrap(value)}`);
136
136
  }
137
137
  const [, oldDefault] = this.children,
138
138
  newDefault = firstElementChild.lastElementChild;
package/src/attribute.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const {typeError, externalUse} = require('../util/debug'),
3
+ const {externalUse} = require('../util/debug'),
4
4
  {toCase, removeComment} = require('../util/string'),
5
5
  /** @type {Parser} */ Parser = require('..'),
6
6
  Token = require('.');
@@ -147,7 +147,7 @@ class AttributeToken extends Token {
147
147
  /** @param {string} key */
148
148
  hasAttr(key) {
149
149
  if (typeof key !== 'string') {
150
- typeError(this, 'hasAttr', 'String');
150
+ this.typeError('hasAttr', 'String');
151
151
  }
152
152
  return this.#attr.has(key.toLowerCase().trim());
153
153
  }
@@ -161,7 +161,7 @@ class AttributeToken extends Token {
161
161
  if (key === undefined) {
162
162
  return Object.fromEntries(this.#attr);
163
163
  } else if (typeof key !== 'string') {
164
- typeError(this, 'getAttr', 'String');
164
+ this.typeError('getAttr', 'String');
165
165
  }
166
166
  return this.#attr.get(key.toLowerCase().trim());
167
167
  }
@@ -182,7 +182,7 @@ class AttributeToken extends Token {
182
182
  setAttr(key, value, init = false) {
183
183
  init &&= !externalUse('setAttr');
184
184
  if (typeof key !== 'string' || !['string', 'boolean'].includes(typeof value)) {
185
- typeError(this, 'setValue', 'String', 'Boolean');
185
+ this.typeError('setValue', 'String', 'Boolean');
186
186
  } else if (!init && this.type === 'ext-attr' && typeof value === 'string' && value.includes('>')) {
187
187
  throw new RangeError('扩展标签属性不能包含 ">"!');
188
188
  }
@@ -214,7 +214,7 @@ class AttributeToken extends Token {
214
214
  */
215
215
  removeAttr(key) {
216
216
  if (typeof key !== 'string') {
217
- typeError(this, 'removeAttr', 'String');
217
+ this.typeError('removeAttr', 'String');
218
218
  }
219
219
  key = key.toLowerCase().trim();
220
220
  if (this.#attr.delete(key)) {
@@ -229,7 +229,7 @@ class AttributeToken extends Token {
229
229
  */
230
230
  toggleAttr(key, force) {
231
231
  if (typeof key !== 'string') {
232
- typeError(this, 'toggleAttr', 'String');
232
+ this.typeError('toggleAttr', 'String');
233
233
  } else if (force !== undefined) {
234
234
  force = Boolean(force);
235
235
  }
package/src/extLink.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const /** @type {Parser} */ Parser = require('..'),
3
+ const {noWrap} = require('../util/string'),
4
+ /** @type {Parser} */ Parser = require('..'),
4
5
  Token = require('.'),
5
6
  MagicLinkToken = require('./magicLink');
6
7
 
@@ -97,7 +98,7 @@ class ExtLinkToken extends Token {
97
98
  const root = Parser.parse(`[//url ${text}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
98
99
  {childNodes: {length}, firstElementChild} = root;
99
100
  if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.childElementCount !== 2) {
100
- throw new SyntaxError(`非法的外链文字:${text.replaceAll('\n', '\\n')}`);
101
+ throw new SyntaxError(`非法的外链文字:${noWrap(text)}`);
101
102
  }
102
103
  const {lastChild} = firstElementChild;
103
104
  if (this.childElementCount === 1) {
package/src/heading.js CHANGED
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const {typeError} = require('../util/debug'),
4
- fixedToken = require('../mixin/fixedToken'),
3
+ const fixedToken = require('../mixin/fixedToken'),
5
4
  /** @type {Parser} */ Parser = require('..'),
6
5
  Token = require('.');
7
6
 
@@ -76,7 +75,7 @@ class HeadingToken extends fixedToken(Token) {
76
75
  /** @param {number} n */
77
76
  setLevel(n) {
78
77
  if (typeof n !== 'number') {
79
- typeError(this, 'setLevel', 'Number');
78
+ this.typeError('setLevel', 'Number');
80
79
  }
81
80
  n = Math.min(Math.max(n, 1), 6);
82
81
  this.setAttribute('name', String(n)).firstElementChild.setAttribute('name', this.name);
package/src/html.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const fixedToken = require('../mixin/fixedToken'),
3
+ const {noWrap} = require('../util/string'),
4
+ fixedToken = require('../mixin/fixedToken'),
4
5
  attributeParent = require('../mixin/attributeParent'),
5
6
  /** @type {Parser} */ Parser = require('..'),
6
7
  Token = require('.');
@@ -79,7 +80,7 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
79
80
  findMatchingTag() {
80
81
  const {html} = this.getAttribute('config'),
81
82
  {name, parentElement, closing, selfClosing} = this,
82
- string = this.toString().replaceAll('\n', '\\n');
83
+ string = noWrap(this.toString());
83
84
  if (closing && selfClosing) {
84
85
  throw new SyntaxError(`同时闭合和自封闭的标签:${string}`);
85
86
  } else if (html[2].includes(name) || selfClosing && html[1].includes(name)) { // 自封闭标签
@@ -134,7 +135,7 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
134
135
  this.selfClosing = false;
135
136
  this.closing = true;
136
137
  } else {
137
- Parser.warn('无法修复无效自封闭标签', this.toString().replaceAll('\n', '\\n'));
138
+ Parser.warn('无法修复无效自封闭标签', noWrap(this.toString()));
138
139
  throw new Error(`无法修复无效自封闭标签:前文共有 ${imbalance} 个未匹配的闭合标签`);
139
140
  }
140
141
  }
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const {typeError} = require('../util/debug'),
4
- {text, extUrlChar} = require('../util/string'),
3
+ const {text, noWrap, extUrlChar} = require('../util/string'),
5
4
  Title = require('../lib/title'),
6
5
  /** @type {Parser} */ Parser = require('..'),
7
6
  Token = require('.');
@@ -32,7 +31,7 @@ class ImageParameterToken extends Token {
32
31
  if (!value) {
33
32
  return this.#noLink;
34
33
  }
35
- const regex = new RegExp(`(?:${config.protocol}|//)${extUrlChar}`, 'ui');
34
+ const regex = new RegExp(`(?:${config.protocol}|//)${extUrlChar}(?=\x00\\d+t\x7f|$)`, 'ui');
36
35
  if (regex.test(value)) {
37
36
  return value;
38
37
  }
@@ -200,13 +199,13 @@ class ImageParameterToken extends Token {
200
199
  setValue(value) {
201
200
  if (this.#isVoid()) {
202
201
  if (typeof value !== 'boolean') {
203
- typeError(this, 'setValue', 'Boolean');
202
+ this.typeError('setValue', 'Boolean');
204
203
  } else if (value === false) {
205
204
  this.remove();
206
205
  }
207
206
  return;
208
207
  } else if (typeof value !== 'string') {
209
- typeError(this, 'setValue', 'String');
208
+ this.typeError('setValue', 'String');
210
209
  }
211
210
  const root = Parser.parse(`[[File:F|${
212
211
  this.#syntax ? this.#syntax.replace('$1', value) : value
@@ -216,7 +215,7 @@ class ImageParameterToken extends Token {
216
215
  if (length !== 1 || !firstElementChild?.matches('file#File:F')
217
216
  || firstElementChild.childElementCount !== 2 || param.name !== this.name
218
217
  ) {
219
- throw new SyntaxError(`非法的 ${this.name} 参数:${value.replaceAll('\n', '\\n')}`);
218
+ throw new SyntaxError(`非法的 ${this.name} 参数:${noWrap(value)}`);
220
219
  }
221
220
  this.replaceChildren(...param.childNodes);
222
221
  }