wikiparser-node 0.8.1-m → 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.
Files changed (81) hide show
  1. package/README.md +39 -0
  2. package/config/moegirl.json +1 -0
  3. package/i18n/zh-hans.json +44 -0
  4. package/i18n/zh-hant.json +44 -0
  5. package/index.js +264 -10
  6. package/lib/element.js +507 -33
  7. package/lib/node.js +550 -6
  8. package/lib/ranges.js +130 -0
  9. package/lib/text.js +111 -41
  10. package/lib/title.js +28 -5
  11. package/mixin/attributeParent.js +117 -0
  12. package/mixin/fixedToken.js +40 -0
  13. package/mixin/hidden.js +3 -0
  14. package/mixin/singleLine.js +31 -0
  15. package/mixin/sol.js +54 -0
  16. package/package.json +9 -8
  17. package/parser/brackets.js +9 -2
  18. package/parser/commentAndExt.js +3 -5
  19. package/parser/converter.js +1 -0
  20. package/parser/externalLinks.js +1 -0
  21. package/parser/hrAndDoubleUnderscore.js +1 -0
  22. package/parser/html.js +1 -0
  23. package/parser/links.js +6 -5
  24. package/parser/list.js +1 -0
  25. package/parser/magicLinks.js +5 -4
  26. package/parser/quotes.js +1 -0
  27. package/parser/selector.js +177 -0
  28. package/parser/table.js +1 -0
  29. package/src/arg.js +123 -5
  30. package/src/atom/hidden.js +2 -0
  31. package/src/atom/index.js +17 -0
  32. package/src/attribute.js +191 -8
  33. package/src/attributes.js +311 -8
  34. package/src/charinsert.js +97 -0
  35. package/src/converter.js +108 -2
  36. package/src/converterFlags.js +190 -3
  37. package/src/converterRule.js +185 -4
  38. package/src/extLink.js +122 -2
  39. package/src/gallery.js +59 -11
  40. package/src/hasNowiki/index.js +12 -0
  41. package/src/hasNowiki/pre.js +12 -0
  42. package/src/heading.js +57 -6
  43. package/src/html.js +133 -12
  44. package/src/imageParameter.js +232 -38
  45. package/src/imagemap.js +65 -6
  46. package/src/imagemapLink.js +14 -2
  47. package/src/index.js +537 -8
  48. package/src/link/category.js +32 -1
  49. package/src/link/file.js +173 -11
  50. package/src/link/galleryImage.js +63 -5
  51. package/src/link/index.js +268 -9
  52. package/src/magicLink.js +92 -11
  53. package/src/nested/choose.js +1 -0
  54. package/src/nested/combobox.js +1 -0
  55. package/src/nested/index.js +31 -7
  56. package/src/nested/references.js +1 -0
  57. package/src/nowiki/comment.js +27 -3
  58. package/src/nowiki/dd.js +47 -1
  59. package/src/nowiki/doubleUnderscore.js +31 -1
  60. package/src/nowiki/hr.js +20 -1
  61. package/src/nowiki/index.js +25 -3
  62. package/src/nowiki/list.js +5 -2
  63. package/src/nowiki/noinclude.js +14 -0
  64. package/src/nowiki/quote.js +17 -3
  65. package/src/onlyinclude.js +26 -1
  66. package/src/paramTag/index.js +27 -4
  67. package/src/paramTag/inputbox.js +4 -1
  68. package/src/parameter.js +150 -8
  69. package/src/syntax.js +68 -0
  70. package/src/table/index.js +941 -4
  71. package/src/table/td.js +229 -10
  72. package/src/table/tr.js +249 -4
  73. package/src/tagPair/ext.js +36 -9
  74. package/src/tagPair/include.js +24 -0
  75. package/src/tagPair/index.js +51 -2
  76. package/src/transclude.js +547 -29
  77. package/tool/index.js +1202 -0
  78. package/util/debug.js +73 -0
  79. package/util/lint.js +8 -7
  80. package/util/string.js +67 -1
  81. package/config/minimum.json +0 -142
package/src/link/file.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const {explode} = require('../../util/string'),
3
+ const {explode, noWrap} = require('../../util/string'),
4
4
  {generateForChild} = require('../../util/lint'),
5
5
  Parser = require('../..'),
6
6
  LinkToken = require('.'),
@@ -17,6 +17,48 @@ const frame = new Set(['manualthumb', 'frameless', 'framed', 'thumbnail']),
17
17
  class FileToken extends LinkToken {
18
18
  type = 'file';
19
19
 
20
+ /** 图片链接 */
21
+ get link() {
22
+ return this.getArg('link')?.link;
23
+ }
24
+
25
+ set link(value) {
26
+ this.setValue('link', value);
27
+ }
28
+
29
+ /** 图片大小 */
30
+ get size() {
31
+ return this.getArg('width')?.size;
32
+ }
33
+
34
+ /** 图片宽度 */
35
+ get width() {
36
+ return this.size?.width;
37
+ }
38
+
39
+ set width(width) {
40
+ const arg = this.getArg('width');
41
+ if (arg) {
42
+ arg.width = width;
43
+ } else {
44
+ this.setValue('width', width);
45
+ }
46
+ }
47
+
48
+ /** 图片高度 */
49
+ get height() {
50
+ return this.size?.height;
51
+ }
52
+
53
+ set height(height) {
54
+ const arg = this.getArg('width');
55
+ if (arg) {
56
+ arg.height = height;
57
+ } else {
58
+ this.setValue('width', `x${height}`);
59
+ }
60
+ }
61
+
20
62
  /**
21
63
  * @param {string} link 文件名
22
64
  * @param {string|undefined} text 图片参数
@@ -24,19 +66,27 @@ class FileToken extends LinkToken {
24
66
  * @param {string} delimiter `|`
25
67
  * @complexity `n`
26
68
  */
27
- constructor(link, text, title, config = Parser.getConfig(), accum = [], delimiter = '|') {
28
- super(link, undefined, title, config, accum, delimiter);
69
+ constructor(link, text, config = Parser.getConfig(), accum = [], delimiter = '|') {
70
+ super(link, undefined, config, accum, delimiter);
71
+ this.setAttribute('acceptable', {AtomToken: 0, ImageParameterToken: '1:'});
29
72
  this.append(...explode('-{', '}-', '|', text).map(part => new ImageParameterToken(part, config, accum)));
73
+ this.seal(
74
+ ['selfLink', 'interwiki', 'setLangLink', 'setFragment', 'asSelfLink', 'setLinkText', 'pipeTrick'],
75
+ true,
76
+ );
30
77
  }
31
78
 
32
79
  /**
33
80
  * @override
34
81
  * @param {number} start 起始位置
35
82
  */
36
- lint(start = 0) {
83
+ lint(start = this.getAbsoluteIndex()) {
37
84
  const errors = super.lint(start),
38
- args = this.getAllArgs(),
39
- keys = [...new Set(args.map(({name}) => name))],
85
+ args = this.getAllArgs().filter(({childNodes}) => {
86
+ const visibleNodes = childNodes.filter(node => node.text().trim());
87
+ return visibleNodes.length !== 1 || visibleNodes[0].type !== 'arg';
88
+ }),
89
+ keys = [...new Set(args.map(({name}) => name))].filter(key => key !== 'invalid'),
40
90
  frameKeys = keys.filter(key => frame.has(key)),
41
91
  horizAlignKeys = keys.filter(key => horizAlign.has(key)),
42
92
  vertAlignKeys = keys.filter(key => vertAlign.has(key));
@@ -55,24 +105,28 @@ class FileToken extends LinkToken {
55
105
  ];
56
106
  }
57
107
  if (relevantArgs.length > 1) {
58
- errors.push(...relevantArgs.map(arg => generateForChild(arg, rect, `重复的图片${key}参数`)));
108
+ errors.push(...relevantArgs.map(
109
+ arg => generateForChild(arg, rect, Parser.msg('duplicated image $1 parameter', key)),
110
+ ));
59
111
  }
60
112
  }
61
113
  if (frameKeys.size > 1) {
62
114
  errors.push(
63
- ...args.filter(({name}) => frame.has(name)).map(arg => generateForChild(arg, rect, '冲突的图片框架参数')),
115
+ ...args.filter(({name}) => frame.has(name)).map(
116
+ arg => generateForChild(arg, rect, 'conflicting image $1 parameter', 'frame'),
117
+ ),
64
118
  );
65
119
  }
66
120
  if (horizAlignKeys.size > 1) {
67
121
  errors.push(
68
122
  ...args.filter(({name}) => horizAlign.has(name))
69
- .map(arg => generateForChild(arg, rect, '冲突的图片水平对齐参数')),
123
+ .map(arg => generateForChild(arg, rect, 'conflicting image $1 parameter', 'horizontal-alignment')),
70
124
  );
71
125
  }
72
126
  if (vertAlignKeys.size > 1) {
73
127
  errors.push(
74
128
  ...args.filter(({name}) => vertAlign.has(name))
75
- .map(arg => generateForChild(arg, rect, '冲突的图片垂直对齐参数')),
129
+ .map(arg => generateForChild(arg, rect, 'conflicting image $1 parameter', 'vertical-alignment')),
76
130
  );
77
131
  }
78
132
  return errors;
@@ -92,7 +146,9 @@ class FileToken extends LinkToken {
92
146
  * @complexity `n`
93
147
  */
94
148
  getArgs(key) {
95
- return this.getAllArgs().filter(({name}) => key === name);
149
+ return typeof key === 'string'
150
+ ? this.getAllArgs().filter(({name}) => key === name)
151
+ : this.typeError('getArgs', 'String');
96
152
  }
97
153
 
98
154
  /**
@@ -103,6 +159,9 @@ class FileToken extends LinkToken {
103
159
  */
104
160
  #getTypedArgs(keys, type) {
105
161
  const args = this.getAllArgs().filter(({name}) => keys.has(name));
162
+ if (args.length > 1) {
163
+ Parser.warn(`图片 ${this.name} 带有 ${args.length} 个${type}参数,只有最后 1 个 ${args[0].name} 会生效!`);
164
+ }
106
165
  return args;
107
166
  }
108
167
 
@@ -120,6 +179,109 @@ class FileToken extends LinkToken {
120
179
  getVertAlignArgs() {
121
180
  return this.#getTypedArgs(vertAlign, '垂直对齐');
122
181
  }
182
+
183
+ /**
184
+ * 获取生效的指定图片参数
185
+ * @param {string} key 参数名
186
+ * @complexity `n`
187
+ */
188
+ getArg(key) {
189
+ return this.getArgs(key).at(-1);
190
+ }
191
+
192
+ /**
193
+ * 是否具有指定图片参数
194
+ * @param {string} key 参数名
195
+ * @complexity `n`
196
+ */
197
+ hasArg(key) {
198
+ return this.getArgs(key).length > 0;
199
+ }
200
+
201
+ /**
202
+ * 移除指定图片参数
203
+ * @param {string} key 参数名
204
+ * @complexity `n`
205
+ */
206
+ removeArg(key) {
207
+ for (const token of this.getArgs(key)) {
208
+ this.removeChild(token);
209
+ }
210
+ }
211
+
212
+ /**
213
+ * 获取图片参数名
214
+ * @complexity `n`
215
+ */
216
+ getKeys() {
217
+ return this.getAllArgs().map(({name}) => name);
218
+ }
219
+
220
+ /**
221
+ * 获取指定的图片参数值
222
+ * @param {string} key 参数名
223
+ * @complexity `n`
224
+ */
225
+ getValues(key) {
226
+ return this.getArgs(key).map(token => token.getValue());
227
+ }
228
+
229
+ /**
230
+ * 获取生效的指定图片参数值
231
+ * @param {string} key 参数名
232
+ * @complexity `n`
233
+ */
234
+ getValue(key) {
235
+ return this.getArg(key)?.getValue();
236
+ }
237
+
238
+ /**
239
+ * 设置图片参数
240
+ * @param {string} key 参数名
241
+ * @param {string|boolean} value 参数值
242
+ * @complexity `n`
243
+ * @throws `RangeError` 未定义的图片参数
244
+ * @throws `SyntaxError` 非法的参数
245
+ */
246
+ setValue(key, value) {
247
+ if (typeof key !== 'string') {
248
+ this.typeError('setValue', 'String');
249
+ } else if (value === false) {
250
+ this.removeArg(key);
251
+ return;
252
+ }
253
+ const token = this.getArg(key);
254
+ value = value === true ? value : String(value);
255
+ if (token) {
256
+ token.setValue(value);
257
+ return;
258
+ }
259
+ let syntax = '';
260
+ const config = this.getAttribute('config');
261
+ if (key !== 'caption') {
262
+ syntax = Object.entries(config.img).find(([, name]) => name === key)?.[0];
263
+ if (!syntax) {
264
+ throw new RangeError(`未定义的图片参数: ${key}`);
265
+ }
266
+ }
267
+ if (value === true) {
268
+ if (syntax.includes('$1')) {
269
+ this.typeError('setValue', 'Boolean');
270
+ }
271
+ const newArg = Parser.run(() => new ImageParameterToken(syntax, config));
272
+ this.insertAt(newArg);
273
+ return;
274
+ }
275
+ const wikitext = `[[File:F|${syntax ? syntax.replace('$1', value) : value}]]`,
276
+ root = Parser.parse(wikitext, this.getAttribute('include'), 6, config),
277
+ {length, firstChild: file} = root,
278
+ {name, type, length: fileLength, lastChild: imageParameter} = file;
279
+ if (length !== 1 || type !== 'file' || name !== 'File:F' || fileLength !== 2 || imageParameter.name !== key) {
280
+ throw new SyntaxError(`非法的 ${key} 参数:${noWrap(value)}`);
281
+ }
282
+ this.insertAt(imageParameter);
283
+ }
123
284
  }
124
285
 
286
+ Parser.classes.FileToken = __filename;
125
287
  module.exports = FileToken;
@@ -1,6 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const {generateForSelf} = require('../../util/lint'),
4
+ {undo} = require('../../util/debug'),
5
+ singleLine = require('../../mixin/singleLine'),
4
6
  Parser = require('../..'),
5
7
  Token = require('..'),
6
8
  FileToken = require('./file');
@@ -9,16 +11,27 @@ const {generateForSelf} = require('../../util/lint'),
9
11
  * 图片
10
12
  * @classdesc `{childNodes: [AtomToken, ...ImageParameterToken]}`
11
13
  */
12
- class GalleryImageToken extends FileToken {
14
+ class GalleryImageToken extends singleLine(FileToken) {
13
15
  type = 'gallery-image';
14
16
  #invalid = false;
15
17
 
18
+ /** 图片链接 */
19
+ get link() {
20
+ return this.type === 'imagemap-image' ? undefined : super.link;
21
+ }
22
+
23
+ set link(value) {
24
+ if (this.type !== 'imagemap-image') {
25
+ super.link = value;
26
+ }
27
+ }
28
+
16
29
  /**
17
30
  * @param {string} link 图片文件名
18
31
  * @param {string|undefined} text 图片参数
19
32
  * @param {accum} accum
20
33
  */
21
- constructor(link, text, title, config = Parser.getConfig(), accum = []) {
34
+ constructor(link, text, config = Parser.getConfig(), accum = []) {
22
35
  let token;
23
36
  if (text !== undefined) {
24
37
  token = new Token(text, config, true, accum);
@@ -28,17 +41,41 @@ class GalleryImageToken extends FileToken {
28
41
  }
29
42
  accum.splice(accum.indexOf(token), 1);
30
43
  }
31
- super(link, token?.toString(), title, config, accum);
44
+ super(link, token?.toString(), config, accum);
32
45
  this.setAttribute('bracket', false);
46
+ if (!Object.values(config.img).includes('width')) {
47
+ this.seal(['size', 'width', 'height'], true);
48
+ }
33
49
  }
34
50
 
35
51
  /**
36
52
  * @override
53
+ * @throws `Error` 非法的内链目标
54
+ * @throws `Error` 不可更改命名空间
37
55
  */
38
56
  afterBuild() {
39
57
  const initImagemap = this.type === 'imagemap-image',
40
58
  titleObj = this.normalizeTitle(String(this.firstChild), initImagemap ? 0 : 6, true, !initImagemap);
59
+ this.setAttribute('name', titleObj.title);
41
60
  this.#invalid = titleObj.interwiki || titleObj.ns !== 6; // 只用于gallery-image的首次解析
61
+ const /** @type {AstListener} */ linkListener = (e, data) => {
62
+ const {prevTarget} = e;
63
+ if (prevTarget?.type === 'link-target') {
64
+ const name = String(prevTarget),
65
+ imagemap = this.type === 'imagemap-image',
66
+ {title, interwiki, ns, valid} = this.normalizeTitle(name, imagemap ? 0 : 6, true, !imagemap);
67
+ if (!valid) {
68
+ undo(e, data);
69
+ throw new Error(`非法的图片文件名:${name}`);
70
+ } else if (interwiki || ns !== 6) {
71
+ undo(e, data);
72
+ throw new Error(`图片链接不可更改命名空间:${name}`);
73
+ }
74
+ this.setAttribute('name', title);
75
+ this.#invalid = false;
76
+ }
77
+ };
78
+ this.addEventListener(['remove', 'insert', 'replace', 'text'], linkListener);
42
79
  }
43
80
 
44
81
  /** @override */
@@ -50,13 +87,34 @@ class GalleryImageToken extends FileToken {
50
87
  * @override
51
88
  * @param {number} start 起始位置
52
89
  */
53
- lint(start = 0) {
90
+ lint(start = this.getAbsoluteIndex()) {
54
91
  const errors = super.lint(start);
55
92
  if (this.#invalid) {
56
- errors.push(generateForSelf(this, {start}, '无效的图库图片'));
93
+ errors.push(generateForSelf(this, {start}, 'invalid gallery image'));
57
94
  }
58
95
  return errors;
59
96
  }
97
+
98
+ /**
99
+ * @override
100
+ * @param {string} link 链接目标
101
+ * @throws `SyntaxError` 非法的链接目标
102
+ */
103
+ setTarget(link) {
104
+ link = String(link);
105
+ const include = this.getAttribute('include'),
106
+ config = this.getAttribute('config'),
107
+ root = Parser.parse(`<gallery>${link}</gallery>`, include, 1, config),
108
+ {length, firstChild: gallery} = root,
109
+ {type, lastChild: {length: galleryLength, firstChild: image}} = gallery;
110
+ if (length !== 1 || type !== 'ext' || galleryLength !== 1 || image.type !== 'gallery-image') {
111
+ throw new SyntaxError(`非法的图库文件名:${link}`);
112
+ }
113
+ const {firstChild} = image;
114
+ image.destroy(true);
115
+ this.firstChild.safeReplaceWith(firstChild);
116
+ }
60
117
  }
61
118
 
119
+ Parser.classes.GalleryImageToken = __filename;
62
120
  module.exports = GalleryImageToken;