wikiparser-node 0.8.1 → 0.9.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/src/transclude.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const {removeComment, escapeRegExp, text, noWrap, print} = require('../util/string'),
3
+ const {removeComment, escapeRegExp, text, noWrap, print, decodeHtml} = require('../util/string'),
4
4
  {externalUse} = require('../util/debug'),
5
5
  {generateForChild} = require('../util/lint'),
6
6
  Parser = require('..'),
@@ -18,8 +18,9 @@ class TranscludeToken extends Token {
18
18
  modifier = '';
19
19
  /** @type {Record<string, Set<ParameterToken>>} */ #args = {};
20
20
  /** @type {Set<string>} */ #keys = new Set();
21
- #fragment = '';
21
+ #fragment;
22
22
  #valid = true;
23
+ #raw = false;
23
24
 
24
25
  /** 是否存在重复参数 */
25
26
  get duplication() {
@@ -36,14 +37,18 @@ class TranscludeToken extends Token {
36
37
  this.typeError('setModifier', 'String');
37
38
  }
38
39
  const {parserFunction: [,, raw, subst]} = this.getAttribute('config'),
39
- lcModifier = modifier.trimStart().toLowerCase(),
40
- isRaw = raw.includes(lcModifier),
41
- isSubst = subst.includes(lcModifier),
42
- wasRaw = raw.includes(this.modifier.trimStart().toLowerCase());
43
- if (wasRaw && isRaw || !wasRaw && (isSubst || modifier === '')
40
+ lcModifier = removeComment(modifier).trim();
41
+ if (modifier && !lcModifier.endsWith(':')) {
42
+ return false;
43
+ }
44
+ const magicWord = lcModifier.slice(0, -1).toLowerCase(),
45
+ isRaw = raw.includes(magicWord),
46
+ isSubst = subst.includes(magicWord);
47
+ if (this.#raw && isRaw || !this.#raw && (isSubst || modifier === '')
44
48
  || (Parser.running || this.length > 1) && (isRaw || isSubst || modifier === '')
45
49
  ) {
46
50
  this.setAttribute('modifier', modifier);
51
+ this.#raw = isRaw;
47
52
  return Boolean(modifier);
48
53
  }
49
54
  return false;
@@ -60,15 +65,20 @@ class TranscludeToken extends Token {
60
65
  super(undefined, config, true, accum, {
61
66
  AtomToken: 0, SyntaxToken: 0, ParameterToken: '1:',
62
67
  });
63
- const {parserFunction: [insensitive, sensitive, raw]} = config;
64
68
  this.seal('modifier');
65
- if (title.includes(':')) {
66
- const [modifier, ...arg] = title.split(':');
67
- if (this.setModifier(modifier)) {
68
- title = arg.join(':');
69
+ const {parserFunction: [insensitive, sensitive]} = config,
70
+ argSubst = /^(?:\s|\0\d+c\x7F)*\0\d+s\x7F/u.exec(title)?.[0];
71
+ if (argSubst) {
72
+ this.setAttribute('modifier', argSubst);
73
+ title = title.slice(argSubst.length);
74
+ } else if (title.includes(':')) {
75
+ const [modifier, ...arg] = title.split(':'),
76
+ [mt] = /^(?:\s|\0\d+c\x7F)*/u.exec(arg[0] ?? '');
77
+ if (this.setModifier(`${modifier}:${mt}`)) {
78
+ title = arg.join(':').slice(mt.length);
69
79
  }
70
80
  }
71
- if (title.includes(':') || parts.length === 0 && !raw.includes(this.modifier.toLowerCase())) {
81
+ if (title.includes(':') || parts.length === 0 && !this.#raw) {
72
82
  const [magicWord, ...arg] = title.split(':'),
73
83
  cleaned = removeComment(magicWord),
74
84
  name = cleaned[arg.length > 0 ? 'trimStart' : 'trim'](),
@@ -103,7 +113,7 @@ class TranscludeToken extends Token {
103
113
  }
104
114
  }
105
115
  if (this.type === 'template') {
106
- const name = removeComment(title).split('#')[0].trim();
116
+ const name = removeComment(decodeHtml(title)).split('#')[0].trim();
107
117
  if (!name || /\0\d+[eh!+-]\x7F|[<>[\]{}\n]|%[\da-f]{2}/u.test(name)) {
108
118
  accum.pop();
109
119
  throw new SyntaxError(`非法的模板名称:${noWrap(name)}`);
@@ -132,6 +142,9 @@ class TranscludeToken extends Token {
132
142
 
133
143
  /** @override */
134
144
  afterBuild() {
145
+ if (this.modifier.includes('\0')) {
146
+ this.setAttribute('modifier', this.getAttribute('buildFromStr')(this.modifier, 'string'));
147
+ }
135
148
  if (this.isTemplate()) {
136
149
  const isTemplate = this.type === 'template',
137
150
  titleObj = this.normalizeTitle(this.childNodes[isTemplate ? 0 : 1].text(), isTemplate ? 10 : 828);
@@ -182,7 +195,7 @@ class TranscludeToken extends Token {
182
195
  return '';
183
196
  }
184
197
  const {childNodes, firstChild, modifier} = this;
185
- return `{{${modifier}${modifier && ':'}${
198
+ return `{{${modifier}${
186
199
  this.type === 'magic-word'
187
200
  ? `${String(firstChild)}${childNodes.length > 1 ? ':' : ''}${childNodes.slice(1).map(String).join('|')}`
188
201
  : super.toString(selector, '|')
@@ -198,7 +211,7 @@ class TranscludeToken extends Token {
198
211
  const {childNodes, firstChild, modifier, type, name} = this;
199
212
  return type === 'magic-word' && name === 'vardefine'
200
213
  ? ''
201
- : `{{${modifier}${modifier && ':'}${
214
+ : `{{${modifier}${
202
215
  this.type === 'magic-word'
203
216
  ? `${firstChild.text()}${childNodes.length > 1 ? ':' : ''}${text(childNodes.slice(1), '|')}`
204
217
  : super.text('|')
@@ -207,7 +220,7 @@ class TranscludeToken extends Token {
207
220
 
208
221
  /** @override */
209
222
  getPadding() {
210
- return this.modifier ? this.modifier.length + 3 : 2;
223
+ return this.modifier.length + 2;
211
224
  }
212
225
 
213
226
  /** @override */
@@ -218,7 +231,7 @@ class TranscludeToken extends Token {
218
231
  /** @override */
219
232
  print() {
220
233
  const {childNodes, firstChild, modifier} = this;
221
- return `<span class="wpb-${this.type}">{{${modifier}${modifier && ':'}${
234
+ return `<span class="wpb-${this.type}">{{${modifier}${
222
235
  this.type === 'magic-word'
223
236
  ? `${firstChild.print()}${childNodes.length > 1 ? ':' : ''}${print(childNodes.slice(1), {sep: '|'})}`
224
237
  : print(childNodes, {sep: '|'})
@@ -229,25 +242,25 @@ class TranscludeToken extends Token {
229
242
  * @override
230
243
  * @param {number} start 起始位置
231
244
  */
232
- lint(start = 0) {
245
+ lint(start = this.getAbsoluteIndex()) {
233
246
  const errors = super.lint(start),
234
247
  {type, childNodes} = this;
235
248
  let rect;
236
249
  if (!this.isTemplate()) {
237
250
  return errors;
238
- } else if (this.#fragment) {
251
+ } else if (this.#fragment !== undefined) {
239
252
  rect = {start, ...this.getRootNode().posFromIndex(start)};
240
- errors.push(generateForChild(childNodes[type === 'template' ? 0 : 1], rect, '多余的fragment'));
253
+ errors.push(generateForChild(childNodes[type === 'template' ? 0 : 1], rect, 'useless fragment'));
241
254
  }
242
255
  if (!this.#valid) {
243
256
  rect = {start, ...this.getRootNode().posFromIndex(start)};
244
- errors.push(generateForChild(childNodes[1], rect, '非法的模块名称'));
257
+ errors.push(generateForChild(childNodes[1], rect, 'illegal module name'));
245
258
  }
246
259
  const duplicatedArgs = this.getDuplicatedArgs();
247
260
  if (duplicatedArgs.length > 0) {
248
261
  rect ||= {start, ...this.getRootNode().posFromIndex(start)};
249
262
  errors.push(...duplicatedArgs.flatMap(([, args]) => args).map(
250
- arg => generateForChild(arg, rect, '重复参数'),
263
+ arg => generateForChild(arg, rect, 'duplicated parameter'),
251
264
  ));
252
265
  }
253
266
  return errors;
@@ -413,7 +426,11 @@ class TranscludeToken extends Token {
413
426
  config = this.getAttribute('config');
414
427
  return Parser.run(() => {
415
428
  const token = new TranscludeToken(this.type === 'template' ? '' : first.text(), [], config);
416
- token.setModifier(this.modifier);
429
+ if (this.#raw) {
430
+ token.setModifier(this.modifier);
431
+ } else {
432
+ token.setAttribute('modifier', this.modifier);
433
+ }
417
434
  token.firstChild.safeReplaceWith(first);
418
435
  token.afterBuild();
419
436
  token.append(...cloned);
@@ -423,12 +440,12 @@ class TranscludeToken extends Token {
423
440
 
424
441
  /** 替换引用 */
425
442
  subst() {
426
- this.setModifier('subst');
443
+ this.setModifier('subst:');
427
444
  }
428
445
 
429
446
  /** 安全的替换引用 */
430
447
  safesubst() {
431
- this.setModifier('safesubst');
448
+ this.setModifier('safesubst:');
432
449
  }
433
450
 
434
451
  /**
package/util/lint.js CHANGED
@@ -1,16 +1,17 @@
1
1
  'use strict';
2
2
 
3
- const Token = require('../src');
3
+ const Parser = require('..'),
4
+ Token = require('../src');
4
5
 
5
6
  /**
6
7
  * 生成对于子节点的LintError对象
7
8
  * @param {Token} child 子节点
8
9
  * @param {{top: number, left: number, start: number}} boundingRect 父节点的绝对定位
9
- * @param {string} message 错误信息
10
+ * @param {string} msg 错误信息
10
11
  * @param {'error'|'warning'} severity 严重程度
11
12
  * @returns {LintError}
12
13
  */
13
- const generateForChild = (child, boundingRect, message, severity = 'error') => {
14
+ const generateForChild = (child, boundingRect, msg, severity = 'error') => {
14
15
  const index = child.getRelativeIndex(),
15
16
  {offsetHeight, offsetWidth, parentNode, length} = child,
16
17
  {top: offsetTop, left: offsetLeft} = parentNode.posFromIndex(index),
@@ -23,23 +24,23 @@ const generateForChild = (child, boundingRect, message, severity = 'error') => {
23
24
  endLine = startLine + offsetHeight - 1,
24
25
  startCol = offsetTop ? offsetLeft : left + offsetLeft,
25
26
  endCol = offsetHeight > 1 ? offsetWidth : startCol + offsetWidth;
26
- return {message, severity, startIndex, endIndex, startLine, endLine, startCol, endCol, excerpt};
27
+ return {message: Parser.msg(msg), severity, startIndex, endIndex, startLine, endLine, startCol, endCol, excerpt};
27
28
  };
28
29
 
29
30
  /**
30
31
  * 生成对于自己的LintError对象
31
32
  * @param {Token} token 节点
32
33
  * @param {{top: number, left: number, start: number}} boundingRect 绝对定位
33
- * @param {string} message 错误信息
34
+ * @param {string} msg 错误信息
34
35
  * @param {'error'|'warning'} severity 严重程度
35
36
  * @returns {LintError}
36
37
  */
37
- const generateForSelf = (token, boundingRect, message, severity = 'error') => {
38
+ const generateForSelf = (token, boundingRect, msg, severity = 'error') => {
38
39
  const {start} = boundingRect,
39
40
  {offsetHeight, offsetWidth, length} = token,
40
41
  {top, left} = 'top' in boundingRect ? boundingRect : token.getRootNode().posFromIndex(start);
41
42
  return {
42
- message,
43
+ message: Parser.msg(msg),
43
44
  severity,
44
45
  startIndex: start,
45
46
  endIndex: start + length,
package/util/string.js CHANGED
@@ -72,6 +72,15 @@ const text = (childNodes, separator = '') => {
72
72
  return childNodes.map(child => typeof child === 'string' ? child : child.text()).join(separator);
73
73
  };
74
74
 
75
+ /**
76
+ * decode HTML entities
77
+ * @param {string} str 原字符串
78
+ */
79
+ const decodeHtml = str => str?.replace(
80
+ /&#(\d+|x[\da-f]+);/giu,
81
+ (_, code) => String.fromCodePoint(`${code[0].toLowerCase() === 'x' ? '0' : ''}${code}`),
82
+ );
83
+
75
84
  /**
76
85
  * optionally convert to lower cases
77
86
  * @param {string} val 属性值
@@ -103,5 +112,15 @@ const normalizeSpace = token => {
103
112
  };
104
113
 
105
114
  module.exports = {
106
- extUrlCharFirst, extUrlChar, removeComment, print, escapeRegExp, explode, text, toCase, noWrap, normalizeSpace,
115
+ extUrlCharFirst,
116
+ extUrlChar,
117
+ removeComment,
118
+ print,
119
+ escapeRegExp,
120
+ explode,
121
+ text,
122
+ decodeHtml,
123
+ toCase,
124
+ noWrap,
125
+ normalizeSpace,
107
126
  };