wikiparser-node 0.0.1 → 0.1.0

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/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('.');
@@ -28,14 +28,13 @@ class AttributeToken extends Token {
28
28
  ) {
29
29
  equal = '{{=}}';
30
30
  }
31
- const str = [...this.#attr].map(([k, v]) => {
31
+ return [...this.#attr].map(([k, v]) => {
32
32
  if (v === true) {
33
33
  return k;
34
34
  }
35
35
  const quote = v.includes('"') ? "'" : '"';
36
36
  return `${k}${equal}${quote}${v}${quote}`;
37
37
  }).join(' ');
38
- return str && ` ${str}`;
39
38
  }
40
39
 
41
40
  /** @complexity `n` */
@@ -56,17 +55,17 @@ class AttributeToken extends Token {
56
55
  */
57
56
  #parseAttr() {
58
57
  this.#attr.clear();
59
- const config = this.getAttribute('config'),
60
- include = this.getAttribute('include'),
61
- /** @type {Token & {firstChild: string}} */ token = this.type !== 'ext-attr' && !Parser.running
62
- ? Parser.run(() => new Token(string, config).parseOnce(0, include).parseOnce())
63
- : undefined,
64
- string = removeComment(token?.firstChild ?? this.toString()).replace(/\x00\d+~\x7f/g, '=');
65
- const build = /** @param {string|boolean} str */ str => {
66
- return typeof str === 'boolean' || !(token instanceof Token)
67
- ? str
68
- : token.buildFromStr(str).map(String).join('');
69
- };
58
+ let string = this.toString(),
59
+ /** @type {Token & {firstChild: string}} */ token;
60
+ if (this.type !== 'ext-attr' && !Parser.running) {
61
+ const config = this.getAttribute('config'),
62
+ include = this.getAttribute('include');
63
+ token = Parser.run(() => new Token(string, config).parseOnce(0, include).parseOnce());
64
+ string = token.firstChild;
65
+ }
66
+ string = removeComment(string).replace(/\x00\d+~\x7f/g, '=');
67
+ const build = /** @param {string|boolean} str */ str =>
68
+ typeof str === 'boolean' || !token ? str : token.buildFromStr(str).map(String).join('');
70
69
  for (const [, key,, quoted, unquoted] of string
71
70
  .matchAll(/([^\s/][^\s/=]*)(?:\s*=\s*(?:(["'])(.*?)(?:\2|$)|(\S*)))?/sg)
72
71
  ) {
@@ -147,7 +146,7 @@ class AttributeToken extends Token {
147
146
  /** @param {string} key */
148
147
  hasAttr(key) {
149
148
  if (typeof key !== 'string') {
150
- typeError(this, 'hasAttr', 'String');
149
+ this.typeError('hasAttr', 'String');
151
150
  }
152
151
  return this.#attr.has(key.toLowerCase().trim());
153
152
  }
@@ -161,7 +160,7 @@ class AttributeToken extends Token {
161
160
  if (key === undefined) {
162
161
  return Object.fromEntries(this.#attr);
163
162
  } else if (typeof key !== 'string') {
164
- typeError(this, 'getAttr', 'String');
163
+ this.typeError('getAttr', 'String');
165
164
  }
166
165
  return this.#attr.get(key.toLowerCase().trim());
167
166
  }
@@ -182,7 +181,7 @@ class AttributeToken extends Token {
182
181
  setAttr(key, value, init = false) {
183
182
  init &&= !externalUse('setAttr');
184
183
  if (typeof key !== 'string' || !['string', 'boolean'].includes(typeof value)) {
185
- typeError(this, 'setValue', 'String', 'Boolean');
184
+ this.typeError('setValue', 'String', 'Boolean');
186
185
  } else if (!init && this.type === 'ext-attr' && typeof value === 'string' && value.includes('>')) {
187
186
  throw new RangeError('扩展标签属性不能包含 ">"!');
188
187
  }
@@ -214,7 +213,7 @@ class AttributeToken extends Token {
214
213
  */
215
214
  removeAttr(key) {
216
215
  if (typeof key !== 'string') {
217
- typeError(this, 'removeAttr', 'String');
216
+ this.typeError('removeAttr', 'String');
218
217
  }
219
218
  key = key.toLowerCase().trim();
220
219
  if (this.#attr.delete(key)) {
@@ -229,7 +228,7 @@ class AttributeToken extends Token {
229
228
  */
230
229
  toggleAttr(key, force) {
231
230
  if (typeof key !== 'string') {
232
- typeError(this, 'toggleAttr', 'String');
231
+ this.typeError('toggleAttr', 'String');
233
232
  } else if (force !== undefined) {
234
233
  force = Boolean(force);
235
234
  }
@@ -241,13 +240,24 @@ class AttributeToken extends Token {
241
240
  this.setAttr(key, force === true || force === undefined && value === false);
242
241
  }
243
242
 
243
+ #leadingSpace(str = super.toString()) {
244
+ return str && !/^\s/.test(str) ? ' ' : '';
245
+ }
246
+
244
247
  toString() {
245
- const str = super.toString();
248
+ let str = super.toString();
249
+ str = `${this.#leadingSpace(str)}${str}`;
246
250
  return this.type === 'table-attr' ? str.replaceAll('\n', ' ') : str;
247
251
  }
248
252
 
253
+ getPadding() {
254
+ return this.#leadingSpace().length;
255
+ }
256
+
249
257
  text() {
250
- return this.#updateFromAttr();
258
+ let str = this.#updateFromAttr();
259
+ str = `${this.#leadingSpace(str)}${str}`;
260
+ return this.type === 'table-attr' ? str.replaceAll('\n', ' ') : str;
251
261
  }
252
262
 
253
263
  /** @returns {[number, string][]} */
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
 
@@ -12,16 +13,13 @@ class ExtLinkToken extends Token {
12
13
  type = 'ext-link';
13
14
  #space;
14
15
 
15
- /** @this {ExtLinkToken & {firstElementChild: MagicLinkToken}} */
16
+ /** @this {{firstChild: MagicLinkToken}} */
16
17
  get protocol() {
17
- return this.firstElementChild.protocol;
18
+ return this.firstChild.protocol;
18
19
  }
19
- /**
20
- * @this {ExtLinkToken & {firstElementChild: MagicLinkToken}}
21
- * @param {string} value
22
- */
20
+ /** @this {{firstChild: MagicLinkToken}} */
23
21
  set protocol(value) {
24
- this.firstElementChild.protocol = value;
22
+ this.firstChild.protocol = value;
25
23
  }
26
24
 
27
25
  /**
@@ -51,15 +49,27 @@ class ExtLinkToken extends Token {
51
49
  }
52
50
  }
53
51
 
52
+ #correct() {
53
+ if (!this.#space
54
+ // 都替换成`<`肯定不对,但无妨
55
+ && /^[^[\]<>"\x00-\x20\x7f\p{Zs}\ufffd]/u.test(this.children[1]?.text().replace(/&[lg]t;/, '<'))
56
+ ) {
57
+ this.#space = ' ';
58
+ }
59
+ }
60
+
54
61
  toString() {
62
+ this.#correct();
55
63
  return `[${this.firstElementChild.toString()}${this.#space}${this.children[1]?.toString() ?? ''}]`;
56
64
  }
57
65
 
58
66
  getPadding() {
67
+ this.#correct();
59
68
  return 1;
60
69
  }
61
70
 
62
71
  getGaps() {
72
+ this.#correct();
63
73
  return this.#space.length;
64
74
  }
65
75
 
@@ -100,7 +110,7 @@ class ExtLinkToken extends Token {
100
110
  const root = Parser.parse(`[//url ${text}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
101
111
  {childNodes: {length}, firstElementChild} = root;
102
112
  if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.childElementCount !== 2) {
103
- throw new SyntaxError(`非法的外链文字:${text.replaceAll('\n', '\\n')}`);
113
+ throw new SyntaxError(`非法的外链文字:${noWrap(text)}`);
104
114
  }
105
115
  const {lastChild} = firstElementChild;
106
116
  if (this.childElementCount === 1) {
package/src/heading.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const {typeError} = require('../util/debug'),
4
- fixedToken = require('../mixin/fixedToken'),
3
+ const fixedToken = require('../mixin/fixedToken'),
4
+ sol = require('../mixin/sol'),
5
5
  /** @type {Parser} */ Parser = require('..'),
6
6
  Token = require('.');
7
7
 
@@ -9,7 +9,7 @@ const {typeError} = require('../util/debug'),
9
9
  * 章节标题
10
10
  * @classdesc `{childNodes: [Token, HiddenToken]}`
11
11
  */
12
- class HeadingToken extends fixedToken(Token) {
12
+ class HeadingToken extends fixedToken(sol(Token)) {
13
13
  type = 'heading';
14
14
 
15
15
  /**
@@ -38,34 +38,29 @@ class HeadingToken extends fixedToken(Token) {
38
38
  return token;
39
39
  }
40
40
 
41
+ /** @this {HeadingToken & {prependNewLine(): ''|'\n', appendNewLine(): ''|'\n'}} */
41
42
  toString() {
42
- const equals = '='.repeat(Number(this.name)),
43
- {previousVisibleSibling, nextVisibleSibling} = this;
44
- return `${
45
- typeof previousVisibleSibling === 'string' && !previousVisibleSibling.endsWith('\n')
46
- || previousVisibleSibling instanceof Token
47
- ? '\n'
48
- : ''
49
- }${equals}${super.toString(equals)}${
50
- typeof nextVisibleSibling === 'string' && !nextVisibleSibling.startsWith('\n')
51
- || nextVisibleSibling instanceof Token
52
- ? '\n'
53
- : ''
54
- }`;
43
+ const equals = '='.repeat(Number(this.name));
44
+ return `${this.prependNewLine()}${equals}${
45
+ this.firstElementChild.toString()
46
+ }${equals}${this.lastElementChild.toString()}${this.appendNewLine()}`;
55
47
  }
56
48
 
57
49
  getPadding() {
58
- return Number(this.name);
50
+ return super.getPadding() + Number(this.name);
59
51
  }
60
52
 
61
53
  getGaps() {
62
54
  return Number(this.name);
63
55
  }
64
56
 
65
- /** @returns {string} */
57
+ /**
58
+ * @this {HeadingToken & {prependNewLine(): ''|'\n', appendNewLine(): ''|'\n'}}
59
+ * @returns {string}
60
+ */
66
61
  text() {
67
62
  const equals = '='.repeat(Number(this.name));
68
- return `${equals}${this.firstElementChild.text()}${equals}`;
63
+ return `${this.prependNewLine()}${equals}${this.firstElementChild.text()}${equals}${this.appendNewLine()}`;
69
64
  }
70
65
 
71
66
  /** @returns {[number, string][]} */
@@ -76,7 +71,7 @@ class HeadingToken extends fixedToken(Token) {
76
71
  /** @param {number} n */
77
72
  setLevel(n) {
78
73
  if (typeof n !== 'number') {
79
- typeError(this, 'setLevel', 'Number');
74
+ this.typeError('setLevel', 'Number');
80
75
  }
81
76
  n = Math.min(Math.max(n, 1), 6);
82
77
  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
- {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('.');
@@ -14,9 +13,13 @@ class ImageParameterToken extends Token {
14
13
  type = 'image-parameter';
15
14
  #syntax = '';
16
15
 
16
+ static #noLink = Symbol('no-link');
17
+
17
18
  /**
18
- * @param {string} key
19
+ * @template {string} T
20
+ * @param {T} key
19
21
  * @param {string} value
22
+ * @returns {T extends 'link' ? string|Symbol : boolean}
20
23
  */
21
24
  static #validate(key, value, config = Parser.getConfig()) {
22
25
  value = value.replace(/\x00\d+t\x7f/g, '').trim();
@@ -26,11 +29,11 @@ class ImageParameterToken extends Token {
26
29
  return true;
27
30
  } else if (key === 'link') {
28
31
  if (!value) {
29
- return true;
32
+ return this.#noLink;
30
33
  }
31
- const regex = new RegExp(`(?:${config.protocol}|//)${extUrlChar}`, 'ui');
34
+ const regex = new RegExp(`(?:${config.protocol}|//)${extUrlChar}(?=\x00\\d+t\x7f|$)`, 'ui');
32
35
  if (regex.test(value)) {
33
- return true;
36
+ return value;
34
37
  }
35
38
  if (/^\[\[.+]]$/.test(value)) {
36
39
  value = value.slice(2, -2);
@@ -40,11 +43,62 @@ class ImageParameterToken extends Token {
40
43
  value = decodeURIComponent(value);
41
44
  } catch {}
42
45
  }
43
- return new Title(value, 0, config).valid;
46
+ const {title, fragment, valid} = new Title(value, 0, config);
47
+ return valid && `${title}${fragment && '#'}${fragment}`;
44
48
  }
45
49
  return !isNaN(value);
46
50
  }
47
51
 
52
+ get link() {
53
+ if (this.name === 'link') {
54
+ return ImageParameterToken.#validate('link', this.getValue(), this.getAttribute('config'));
55
+ }
56
+ return undefined;
57
+ }
58
+ set link(value) {
59
+ if (this.name === 'link') {
60
+ value = value === ImageParameterToken.#noLink ? '' : value;
61
+ this.setValue(value);
62
+ }
63
+ }
64
+ get size() {
65
+ if (this.name === 'width') {
66
+ const /** @type {string} */ size = this.getValue().trim();
67
+ if (!size.includes('{{')) {
68
+ const [width, height = ''] = size.split('x');
69
+ return {width, height};
70
+ }
71
+ const token = Parser.parse(size, false, 2, this.getAttribute('config')),
72
+ {childNodes} = token,
73
+ i = childNodes.findIndex(child => typeof child === 'string' && child.includes('x'));
74
+ if (i === -1) {
75
+ return {width: size, height: ''};
76
+ }
77
+ token.splitText(i, childNodes[i].indexOf('x'));
78
+ token.splitText(i + 1, 1);
79
+ return {width: text(token.childNodes.slice(0, i + 1)), height: text(token.childNodes.slice(i + 2))};
80
+ }
81
+ return undefined;
82
+ }
83
+ get width() {
84
+ return this.size?.width;
85
+ }
86
+ set width(width) {
87
+ if (this.name === 'width') {
88
+ const {height} = this;
89
+ this.setValue(`${String(width || '')}${height && 'x'}${height}`);
90
+ }
91
+ }
92
+ get height() {
93
+ return this.size?.height;
94
+ }
95
+ set height(height) {
96
+ height = String(height || '');
97
+ if (this.name === 'width') {
98
+ this.setValue(`${this.width}${height && 'x'}${height}`);
99
+ }
100
+ }
101
+
48
102
  /**
49
103
  * @param {string} str
50
104
  * @param {accum} accum
@@ -145,13 +199,13 @@ class ImageParameterToken extends Token {
145
199
  setValue(value) {
146
200
  if (this.#isVoid()) {
147
201
  if (typeof value !== 'boolean') {
148
- typeError(this, 'setValue', 'Boolean');
202
+ this.typeError('setValue', 'Boolean');
149
203
  } else if (value === false) {
150
204
  this.remove();
151
205
  }
152
206
  return;
153
207
  } else if (typeof value !== 'string') {
154
- typeError(this, 'setValue', 'String');
208
+ this.typeError('setValue', 'String');
155
209
  }
156
210
  const root = Parser.parse(`[[File:F|${
157
211
  this.#syntax ? this.#syntax.replace('$1', value) : value
@@ -161,7 +215,7 @@ class ImageParameterToken extends Token {
161
215
  if (length !== 1 || !firstElementChild?.matches('file#File:F')
162
216
  || firstElementChild.childElementCount !== 2 || param.name !== this.name
163
217
  ) {
164
- throw new SyntaxError(`非法的 ${this.name} 参数:${value.replaceAll('\n', '\\n')}`);
218
+ throw new SyntaxError(`非法的 ${this.name} 参数:${noWrap(value)}`);
165
219
  }
166
220
  this.replaceChildren(...param.childNodes);
167
221
  }
package/src/index.js CHANGED
@@ -37,9 +37,10 @@
37
37
  * l: LinkToken
38
38
  * q: QuoteToken
39
39
  * w: ExtLinkToken
40
+ * d: ListToken
40
41
  */
41
42
 
42
- const {typeError, externalUse} = require('../util/debug'),
43
+ const {externalUse} = require('../util/debug'),
43
44
  Ranges = require('../lib/ranges'),
44
45
  AstElement = require('../lib/element'),
45
46
  assert = require('assert/strict'),
@@ -255,7 +256,7 @@ class Token extends AstElement {
255
256
  if (!parentNode) {
256
257
  throw new Error('不存在父节点!');
257
258
  } else if (token.constructor !== this.constructor) {
258
- typeError(this, 'safeReplaceWith', this.constructor.name);
259
+ this.typeError('safeReplaceWith', this.constructor.name);
259
260
  }
260
261
  try {
261
262
  assert.deepEqual(token.getAttribute('acceptable'), this.#acceptable);
@@ -320,7 +321,7 @@ class Token extends AstElement {
320
321
  */
321
322
  section(n) {
322
323
  if (typeof n !== 'number') {
323
- typeError(this, 'section', 'Number');
324
+ this.typeError('section', 'Number');
324
325
  }
325
326
  return this.sections()[n];
326
327
  }
@@ -332,7 +333,7 @@ class Token extends AstElement {
332
333
  */
333
334
  findEnclosingHtml(tag) {
334
335
  if (tag !== undefined && typeof tag !== 'string') {
335
- typeError(this, 'findEnclosingHtml', 'String');
336
+ this.typeError('findEnclosingHtml', 'String');
336
337
  }
337
338
  tag = tag?.toLowerCase();
338
339
  if (tag !== undefined && !this.#config.html.slice(0, 2).flat().includes(tag)) {
@@ -436,11 +437,7 @@ class Token extends AstElement {
436
437
  this.#parseLinks();
437
438
  break;
438
439
  case 6: {
439
- const lines = this.firstChild.split('\n');
440
- for (let i = 0; i < lines.length; i++) {
441
- lines[i] = this.#parseQuotes(lines[i]);
442
- }
443
- this.setText(lines.join('\n'));
440
+ this.#parseQuotes();
444
441
  break;
445
442
  }
446
443
  case 7:
@@ -450,6 +447,7 @@ class Token extends AstElement {
450
447
  this.#parseMagicLinks();
451
448
  break;
452
449
  case 9:
450
+ this.#parseList();
453
451
  break;
454
452
  case 10:
455
453
  // no default
@@ -519,7 +517,7 @@ class Token extends AstElement {
519
517
  /** 解析、重构、生成部分Token的`name`属性 */
520
518
  parse(n = MAX_STAGE, include = false) {
521
519
  if (typeof n !== 'number') {
522
- typeError(this, 'parse', 'Number');
520
+ this.typeError('parse', 'Number');
523
521
  } else if (n < MAX_STAGE && !Parser.debugging && externalUse('parse')) {
524
522
  Parser.warn('指定解析层级的方法仅供熟练用户使用!');
525
523
  }
@@ -579,10 +577,14 @@ class Token extends AstElement {
579
577
  this.setText(parseLinks(this.firstChild, this.#config, this.#accum));
580
578
  }
581
579
 
582
- /** @param {string} text */
583
- #parseQuotes(text) {
584
- const parseQuotes = require('../parser/quotes');
585
- return parseQuotes(text, this.#config, this.#accum);
580
+ /** @this {Token & {firstChild: string}} */
581
+ #parseQuotes() {
582
+ const parseQuotes = require('../parser/quotes'),
583
+ lines = this.firstChild.split('\n');
584
+ for (let i = 0; i < lines.length; i++) {
585
+ lines[i] = parseQuotes(lines[i], this.#config, this.#accum);
586
+ }
587
+ this.setText(lines.join('\n'));
586
588
  }
587
589
 
588
590
  /** @this {Token & {firstChild: string}} */
@@ -596,6 +598,15 @@ class Token extends AstElement {
596
598
  const parseMagicLinks = require('../parser/magicLinks');
597
599
  this.setText(parseMagicLinks(this.firstChild, this.#config, this.#accum));
598
600
  }
601
+
602
+ #parseList() {
603
+ const parseList = require('../parser/list'),
604
+ lines = this.firstChild.split('\n');
605
+ for (let i = 0; i < lines.length; i++) {
606
+ lines[i] = parseList(lines[i], this.#config, this.#accum);
607
+ }
608
+ this.setText(lines.join('\n'));
609
+ }
599
610
  }
600
611
 
601
612
  Parser.classes.Token = __filename;
package/src/link/file.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const {explode} = require('../../util/string'),
4
- {typeError, externalUse} = require('../../util/debug'),
3
+ const {explode, noWrap} = require('../../util/string'),
4
+ {externalUse} = require('../../util/debug'),
5
5
  /** @type {Parser} */ Parser = require('../..'),
6
6
  LinkToken = require('.'),
7
7
  ImageParameterToken = require('../imageParameter');
@@ -20,6 +20,38 @@ class FileToken extends LinkToken {
20
20
  setLinkText = undefined;
21
21
  pipeTrick = undefined;
22
22
 
23
+ get link() {
24
+ return this.getArg('link')?.link;
25
+ }
26
+ set link(value) {
27
+ this.setValue('link', value);
28
+ }
29
+ get size() {
30
+ return this.getArg('width')?.size;
31
+ }
32
+ get width() {
33
+ return this.size?.width;
34
+ }
35
+ set width(width) {
36
+ const arg = this.getArg('width');
37
+ if (arg) {
38
+ arg.width = width;
39
+ } else {
40
+ this.setValue('width', width);
41
+ }
42
+ }
43
+ get height() {
44
+ return this.size?.height;
45
+ }
46
+ set height(height) {
47
+ const arg = this.getArg('width');
48
+ if (arg) {
49
+ arg.height = height;
50
+ } else {
51
+ this.setValue('width', `x${height}`);
52
+ }
53
+ }
54
+
23
55
  /**
24
56
  * @param {string} link
25
57
  * @param {string|undefined} text
@@ -70,7 +102,7 @@ class FileToken extends LinkToken {
70
102
  const args = this.getAllArgs()
71
103
  .filter(({name}) => ['manualthumb', 'frameless', 'framed', 'thumbnail'].includes(name));
72
104
  if (args.length > 1) {
73
- Parser.error(`警告:图片 ${this.name} 带有 ${args.length} 个框架参数,只有第 1 个 ${args[0].name} 会生效!`);
105
+ Parser.error(`图片 ${this.name} 带有 ${args.length} 个框架参数,只有第 1 个 ${args[0].name} 会生效!`);
74
106
  }
75
107
  return args;
76
108
  }
@@ -81,7 +113,7 @@ class FileToken extends LinkToken {
81
113
  */
82
114
  getArgs(key, copy = true) {
83
115
  if (typeof key !== 'string') {
84
- typeError(this, 'getArgs', 'String');
116
+ this.typeError('getArgs', 'String');
85
117
  } else if (!copy && !Parser.debugging && externalUse('getArgs')) {
86
118
  this.debugOnly('getArgs');
87
119
  }
@@ -158,7 +190,7 @@ class FileToken extends LinkToken {
158
190
  */
159
191
  setValue(key, value) {
160
192
  if (typeof key !== 'string') {
161
- typeError(this, 'setValue', 'String');
193
+ this.typeError('setValue', 'String');
162
194
  } else if (value === false) {
163
195
  this.removeArg(key);
164
196
  return;
@@ -179,7 +211,7 @@ class FileToken extends LinkToken {
179
211
  }
180
212
  if (value === true) {
181
213
  if (syntax.includes('$1')) {
182
- typeError(this, 'setValue', 'Boolean');
214
+ this.typeError('setValue', 'Boolean');
183
215
  }
184
216
  const newArg = Parser.run(() => new ImageParameterToken(syntax, config));
185
217
  this.appendChild(newArg);
@@ -191,7 +223,7 @@ class FileToken extends LinkToken {
191
223
  if (length !== 1 || !firstElementChild?.matches('file#File:F')
192
224
  || firstElementChild.childElementCount !== 2 || firstElementChild.lastElementChild.name !== key
193
225
  ) {
194
- throw new SyntaxError(`非法的 ${key} 参数:${value.replaceAll('\n', '\\n')}`);
226
+ throw new SyntaxError(`非法的 ${key} 参数:${noWrap(value)}`);
195
227
  }
196
228
  this.appendChild(firstElementChild.lastChild);
197
229
  }
package/src/link/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const Title = require('../../lib/title'), // eslint-disable-line no-unused-vars
4
- {text} = require('../../util/string'),
4
+ {text, noWrap} = require('../../util/string'),
5
5
  {undo} = require('../../util/debug'),
6
6
  /** @type {Parser} */ Parser = require('../..'),
7
7
  Token = require('..');
@@ -170,9 +170,7 @@ class LinkToken extends Token {
170
170
  }L|${linkText}]]`, this.getAttribute('include'), 6, config),
171
171
  {childNodes: {length}, firstElementChild} = root;
172
172
  if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.childElementCount !== 2) {
173
- throw new SyntaxError(`非法的${this.type === 'link' ? '内链文字' : '分类关键字'}:${
174
- linkText.replaceAll('\n', '\\n')
175
- }`);
173
+ throw new SyntaxError(`非法的${this.type === 'link' ? '内链文字' : '分类关键字'}:${noWrap(linkText)}`);
176
174
  }
177
175
  ({lastElementChild} = firstElementChild);
178
176
  } else {