wikiparser-node 0.0.0 → 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/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
 
@@ -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
  /**
@@ -52,7 +50,7 @@ class ExtLinkToken extends Token {
52
50
  }
53
51
 
54
52
  toString() {
55
- return `[${super.toString(this.#space)}]`;
53
+ return `[${this.firstElementChild.toString()}${this.#space}${this.children[1]?.toString() ?? ''}]`;
56
54
  }
57
55
 
58
56
  getPadding() {
@@ -100,7 +98,7 @@ class ExtLinkToken extends Token {
100
98
  const root = Parser.parse(`[//url ${text}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
101
99
  {childNodes: {length}, firstElementChild} = root;
102
100
  if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.childElementCount !== 2) {
103
- throw new SyntaxError(`非法的外链文字:${text.replaceAll('\n', '\\n')}`);
101
+ throw new SyntaxError(`非法的外链文字:${noWrap(text)}`);
104
102
  }
105
103
  const {lastChild} = firstElementChild;
106
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
- {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,24 +13,27 @@ 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
- value = value.trim();
25
+ value = value.replace(/\x00\d+t\x7f/g, '').trim();
23
26
  if (key === 'width') {
24
- const mt = value.match(/^(\d*)(?:x(\d*))?$/);
25
- return Number(mt?.[1]) > 0 || Number(mt?.[2]) > 0;
27
+ return /^\d*(?:x\d*)?$/.test(value);
26
28
  } else if (['alt', 'class', 'manualthumb', 'frameless', 'framed', 'thumbnail'].includes(key)) {
27
29
  return true;
28
30
  } else if (key === 'link') {
29
31
  if (!value) {
30
- return true;
32
+ return this.#noLink;
31
33
  }
32
- const regex = new RegExp(`(?:${config.protocol}|//)${extUrlChar}`, 'ui');
34
+ const regex = new RegExp(`(?:${config.protocol}|//)${extUrlChar}(?=\x00\\d+t\x7f|$)`, 'ui');
33
35
  if (regex.test(value)) {
34
- return true;
36
+ return value;
35
37
  }
36
38
  if (/^\[\[.+]]$/.test(value)) {
37
39
  value = value.slice(2, -2);
@@ -41,11 +43,62 @@ class ImageParameterToken extends Token {
41
43
  value = decodeURIComponent(value);
42
44
  } catch {}
43
45
  }
44
- return new Title(value, 0, config).valid;
46
+ const {title, fragment, valid} = new Title(value, 0, config);
47
+ return valid && `${title}${fragment && '#'}${fragment}`;
45
48
  }
46
49
  return !isNaN(value);
47
50
  }
48
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
+
49
102
  /**
50
103
  * @param {string} str
51
104
  * @param {accum} accum
@@ -68,7 +121,7 @@ class ImageParameterToken extends Token {
68
121
  super(mt[2], config, true, accum, {'Stage-2': ':', '!HeadingToken': ':'});
69
122
  this.#syntax = `${mt[1]}${param[0]}${mt[3]}`;
70
123
  }
71
- this.setAttribute('name', param[1]).setAttribute('stage', 7);
124
+ this.setAttribute('name', param[1]).setAttribute('stage', Parser.MAX_STAGE);
72
125
  return;
73
126
  }
74
127
  }
@@ -146,13 +199,13 @@ class ImageParameterToken extends Token {
146
199
  setValue(value) {
147
200
  if (this.#isVoid()) {
148
201
  if (typeof value !== 'boolean') {
149
- typeError(this, 'setValue', 'Boolean');
202
+ this.typeError('setValue', 'Boolean');
150
203
  } else if (value === false) {
151
204
  this.remove();
152
205
  }
153
206
  return;
154
207
  } else if (typeof value !== 'string') {
155
- typeError(this, 'setValue', 'String');
208
+ this.typeError('setValue', 'String');
156
209
  }
157
210
  const root = Parser.parse(`[[File:F|${
158
211
  this.#syntax ? this.#syntax.replace('$1', value) : value
@@ -162,7 +215,7 @@ class ImageParameterToken extends Token {
162
215
  if (length !== 1 || !firstElementChild?.matches('file#File:F')
163
216
  || firstElementChild.childElementCount !== 2 || param.name !== this.name
164
217
  ) {
165
- throw new SyntaxError(`非法的 ${this.name} 参数:${value.replaceAll('\n', '\\n')}`);
218
+ throw new SyntaxError(`非法的 ${this.name} 参数:${noWrap(value)}`);
166
219
  }
167
220
  this.replaceChildren(...param.childNodes);
168
221
  }
package/src/index.js CHANGED
@@ -39,7 +39,7 @@
39
39
  * w: ExtLinkToken
40
40
  */
41
41
 
42
- const {typeError, externalUse} = require('../util/debug'),
42
+ const {externalUse} = require('../util/debug'),
43
43
  Ranges = require('../lib/ranges'),
44
44
  AstElement = require('../lib/element'),
45
45
  assert = require('assert/strict'),
@@ -255,7 +255,7 @@ class Token extends AstElement {
255
255
  if (!parentNode) {
256
256
  throw new Error('不存在父节点!');
257
257
  } else if (token.constructor !== this.constructor) {
258
- typeError(this, 'safeReplaceWith', this.constructor.name);
258
+ this.typeError('safeReplaceWith', this.constructor.name);
259
259
  }
260
260
  try {
261
261
  assert.deepEqual(token.getAttribute('acceptable'), this.#acceptable);
@@ -320,7 +320,7 @@ class Token extends AstElement {
320
320
  */
321
321
  section(n) {
322
322
  if (typeof n !== 'number') {
323
- typeError(this, 'section', 'Number');
323
+ this.typeError('section', 'Number');
324
324
  }
325
325
  return this.sections()[n];
326
326
  }
@@ -332,7 +332,7 @@ class Token extends AstElement {
332
332
  */
333
333
  findEnclosingHtml(tag) {
334
334
  if (tag !== undefined && typeof tag !== 'string') {
335
- typeError(this, 'findEnclosingHtml', 'String');
335
+ this.typeError('findEnclosingHtml', 'String');
336
336
  }
337
337
  tag = tag?.toLowerCase();
338
338
  if (tag !== undefined && !this.#config.html.slice(0, 2).flat().includes(tag)) {
@@ -519,7 +519,7 @@ class Token extends AstElement {
519
519
  /** 解析、重构、生成部分Token的`name`属性 */
520
520
  parse(n = MAX_STAGE, include = false) {
521
521
  if (typeof n !== 'number') {
522
- typeError(this, 'parse', 'Number');
522
+ this.typeError('parse', 'Number');
523
523
  } else if (n < MAX_STAGE && !Parser.debugging && externalUse('parse')) {
524
524
  Parser.warn('指定解析层级的方法仅供熟练用户使用!');
525
525
  }
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 {
package/src/magicLink.js CHANGED
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const {typeError} = require('../util/debug'),
4
- /** @type {Parser} */ Parser = require('..'),
3
+ const /** @type {Parser} */ Parser = require('..'),
5
4
  Token = require('.');
6
5
 
7
6
  /**
@@ -15,10 +14,9 @@ class MagicLinkToken extends Token {
15
14
  get protocol() {
16
15
  return this.text().match(this.#protocolRegex)?.[0];
17
16
  }
18
- /** @param {string} value */
19
17
  set protocol(value) {
20
18
  if (typeof value !== 'string') {
21
- typeError(this, 'protocol', 'String');
19
+ this.typeError('protocol', 'String');
22
20
  }
23
21
  if (!new RegExp(`${this.#protocolRegex.source}$`, 'i').test(value)) {
24
22
  throw new RangeError(`非法的外链协议:${value}`);
@@ -38,6 +36,14 @@ class MagicLinkToken extends Token {
38
36
  this.#protocolRegex = new RegExp(`^(?:${config.protocol}${doubleSlash ? '|//' : ''})`, 'i');
39
37
  }
40
38
 
39
+ afterBuild() {
40
+ const ParameterToken = require('./parameter'), // eslint-disable-line no-unused-vars
41
+ /** @type {ParameterToken} */ parameter = this.closest('parameter');
42
+ if (parameter?.getValue() === this.text()) {
43
+ this.replaceWith(this.toString());
44
+ }
45
+ }
46
+
41
47
  getUrl() {
42
48
  const url = this.text();
43
49
  try {
@@ -30,7 +30,7 @@ class CommentToken extends hidden(NowikiToken) {
30
30
  toString() {
31
31
  const {firstChild, closed, nextSibling} = this;
32
32
  if (!closed && nextSibling) {
33
- Parser.error('自动闭合HTML注释', firstChild.replaceAll('\n', '\\n'));
33
+ Parser.error('自动闭合HTML注释', this);
34
34
  this.closed = true;
35
35
  }
36
36
  return `<!--${firstChild}${this.closed ? '-->' : ''}`;
package/src/parameter.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const {typeError} = require('../util/debug'),
3
+ const {noWrap} = require('../util/string'),
4
4
  fixedToken = require('../mixin/fixedToken'),
5
5
  /** @type {Parser} */ Parser = require('..'),
6
6
  Token = require('.');
@@ -116,7 +116,7 @@ class ParameterToken extends fixedToken(Token) {
116
116
  || firstElementChild.childElementCount !== 2
117
117
  || lastElementChild.anon !== this.anon || lastElementChild.name !== '1'
118
118
  ) {
119
- throw new SyntaxError(`非法的模板参数:${value.replaceAll('\n', '\\n')}`);
119
+ throw new SyntaxError(`非法的模板参数:${noWrap(value)}`);
120
120
  }
121
121
  const newValue = lastElementChild.lastChild;
122
122
  root.destroy();
@@ -128,7 +128,7 @@ class ParameterToken extends fixedToken(Token) {
128
128
  /** @param {string} key */
129
129
  rename(key, force = false) {
130
130
  if (typeof key !== 'string') {
131
- typeError(this, 'rename', 'String');
131
+ this.typeError('rename', 'String');
132
132
  }
133
133
  const {parentNode} = this;
134
134
  // 必须检测是否是TranscludeToken