wikiparser-node 0.3.0 → 0.4.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/.eslintrc.json +472 -34
  2. package/README.md +1 -1
  3. package/config/default.json +58 -30
  4. package/config/llwiki.json +22 -90
  5. package/config/moegirl.json +51 -13
  6. package/config/zhwiki.json +1269 -0
  7. package/index.js +114 -104
  8. package/lib/element.js +448 -440
  9. package/lib/node.js +335 -115
  10. package/lib/ranges.js +27 -18
  11. package/lib/text.js +146 -0
  12. package/lib/title.js +13 -5
  13. package/mixin/attributeParent.js +70 -24
  14. package/mixin/fixedToken.js +14 -6
  15. package/mixin/hidden.js +6 -4
  16. package/mixin/sol.js +27 -10
  17. package/package.json +9 -3
  18. package/parser/brackets.js +22 -17
  19. package/parser/commentAndExt.js +18 -16
  20. package/parser/converter.js +14 -13
  21. package/parser/externalLinks.js +12 -11
  22. package/parser/hrAndDoubleUnderscore.js +23 -14
  23. package/parser/html.js +10 -9
  24. package/parser/links.js +15 -14
  25. package/parser/list.js +12 -11
  26. package/parser/magicLinks.js +12 -11
  27. package/parser/quotes.js +6 -5
  28. package/parser/selector.js +175 -0
  29. package/parser/table.js +25 -18
  30. package/printed/example.json +120 -0
  31. package/src/arg.js +56 -32
  32. package/src/atom/hidden.js +5 -2
  33. package/src/atom/index.js +17 -9
  34. package/src/attribute.js +182 -100
  35. package/src/converter.js +68 -41
  36. package/src/converterFlags.js +67 -45
  37. package/src/converterRule.js +117 -65
  38. package/src/extLink.js +66 -18
  39. package/src/gallery.js +42 -15
  40. package/src/heading.js +34 -15
  41. package/src/html.js +97 -35
  42. package/src/imageParameter.js +83 -54
  43. package/src/index.js +299 -178
  44. package/src/link/category.js +20 -52
  45. package/src/link/file.js +59 -28
  46. package/src/link/galleryImage.js +21 -7
  47. package/src/link/index.js +146 -60
  48. package/src/magicLink.js +34 -12
  49. package/src/nowiki/comment.js +22 -10
  50. package/src/nowiki/dd.js +37 -22
  51. package/src/nowiki/doubleUnderscore.js +16 -7
  52. package/src/nowiki/hr.js +11 -7
  53. package/src/nowiki/index.js +16 -9
  54. package/src/nowiki/list.js +2 -2
  55. package/src/nowiki/noinclude.js +8 -4
  56. package/src/nowiki/quote.js +11 -7
  57. package/src/onlyinclude.js +19 -7
  58. package/src/parameter.js +65 -38
  59. package/src/syntax.js +26 -20
  60. package/src/table/index.js +260 -165
  61. package/src/table/td.js +98 -52
  62. package/src/table/tr.js +102 -58
  63. package/src/tagPair/ext.js +27 -19
  64. package/src/tagPair/include.js +16 -11
  65. package/src/tagPair/index.js +64 -29
  66. package/src/transclude.js +170 -93
  67. package/test/api.js +83 -0
  68. package/test/real.js +133 -0
  69. package/test/test.js +28 -0
  70. package/test/util.js +80 -0
  71. package/tool/index.js +41 -31
  72. package/typings/api.d.ts +13 -0
  73. package/typings/array.d.ts +28 -0
  74. package/typings/event.d.ts +24 -0
  75. package/typings/index.d.ts +46 -4
  76. package/typings/node.d.ts +15 -9
  77. package/typings/parser.d.ts +7 -0
  78. package/typings/tool.d.ts +3 -2
  79. package/util/debug.js +21 -18
  80. package/util/string.js +40 -27
  81. package/typings/element.d.ts +0 -28
package/src/link/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
- const Title = require('../../lib/title'), // eslint-disable-line no-unused-vars
4
- {text, noWrap} = require('../../util/string'),
3
+ const Title = require('../../lib/title'),
4
+ {noWrap} = require('../../util/string'),
5
5
  {undo} = require('../../util/debug'),
6
- /** @type {Parser} */ Parser = require('../..'),
6
+ Parser = require('../..'),
7
+ AstText = require('../../lib/text'),
7
8
  Token = require('..');
8
9
 
9
10
  /**
@@ -12,14 +13,67 @@ const Title = require('../../lib/title'), // eslint-disable-line no-unused-vars
12
13
  */
13
14
  class LinkToken extends Token {
14
15
  type = 'link';
15
- selfLink;
16
- fragment;
17
- interwiki;
16
+
17
+ /** 完整链接,和FileToken保持一致 */
18
+ get link() {
19
+ return String(this.#getTitle());
20
+ }
21
+
22
+ set link(link) {
23
+ this.setTarget(link);
24
+ }
25
+
26
+ /** 是否链接到自身 */
27
+ get selfLink() {
28
+ return !this.#getTitle().title;
29
+ }
30
+
31
+ set selfLink(selfLink) {
32
+ if (selfLink === true) {
33
+ this.asSelfLink();
34
+ }
35
+ }
36
+
37
+ /** fragment */
38
+ get fragment() {
39
+ return this.#getTitle().fragment;
40
+ }
41
+
42
+ set fragment(fragment) {
43
+ this.setFragment(fragment);
44
+ }
45
+
46
+ /** interwiki */
47
+ get interwiki() {
48
+ return this.#getTitle().interwiki;
49
+ }
50
+
51
+ set interwiki(interwiki) {
52
+ if (typeof interwiki !== 'string') {
53
+ this.typeError('set interwiki', 'String');
54
+ }
55
+ const {prefix, main, fragment} = this.#getTitle(),
56
+ link = `${interwiki}:${prefix}${main}${fragment && '#'}${fragment}`;
57
+ if (interwiki && !this.isInterwiki(link)) {
58
+ throw new RangeError(`${interwiki} 不是合法的跨维基前缀!`);
59
+ }
60
+ this.setTarget(link);
61
+ }
62
+
63
+ /** 链接显示文字 */
64
+ get innerText() {
65
+ if (this.type !== 'link') {
66
+ return undefined;
67
+ }
68
+ return this.childNodes.length > 1
69
+ ? this.lastElementChild.text()
70
+ : this.firstElementChild.text().replace(/^\s*:/u, '');
71
+ }
18
72
 
19
73
  /**
20
- * @param {string} link
21
- * @param {string|undefined} linkText
22
- * @param {Title} title
74
+ * @param {string} link 链接标题
75
+ * @param {string|undefined} linkText 链接显示文字
76
+ * @param {Title} title 链接标题对象
23
77
  * @param {accum} accum
24
78
  */
25
79
  constructor(link, linkText, title, config = Parser.getConfig(), accum = []) {
@@ -33,84 +87,94 @@ class LinkToken extends Token {
33
87
  inner.type = 'link-text';
34
88
  this.appendChild(inner.setAttribute('stage', Parser.MAX_STAGE - 1));
35
89
  }
36
- this.selfLink = !title.title;
37
- this.fragment = title.fragment;
38
- this.interwiki = title.interwiki;
39
- this.setAttribute('name', title.title).seal(['selfLink', 'fragment', 'interwiki']).protectChildren(0);
90
+ this.setAttribute('name', title.title).getAttribute('protectChildren')(0);
40
91
  }
41
92
 
93
+ /** 生成Title对象 */
94
+ #getTitle() {
95
+ return this.normalizeTitle(this.firstElementChild.text());
96
+ }
97
+
98
+ /** @override */
42
99
  cloneNode() {
43
- const [link, ...linkText] = this.cloneChildren();
100
+ const [link, ...linkText] = this.cloneChildNodes();
44
101
  return Parser.run(() => {
45
- const /** @type {typeof LinkToken} */ Constructor = this.constructor,
46
- token = new Constructor('', undefined, {
47
- title: this.name, interwiki: this.interwiki, fragment: this.fragment,
48
- }, this.getAttribute('config'));
102
+ /** @type {this & {constructor: typeof LinkToken}} */
103
+ const {constructor} = this,
104
+ token = new constructor('', undefined, this.#getTitle(), this.getAttribute('config'));
49
105
  token.firstElementChild.safeReplaceWith(link);
50
106
  token.append(...linkText);
51
107
  return token.afterBuild();
52
108
  });
53
109
  }
54
110
 
111
+ /**
112
+ * @override
113
+ * @throws `Error` 非法的内链目标
114
+ * @throws `Error` 不可更改命名空间
115
+ */
55
116
  afterBuild() {
56
- if (this.name.includes('\x00')) {
57
- this.setAttribute('name', text(this.buildFromStr(this.name)));
58
- }
59
- if (this.fragment.includes('\x00')) {
60
- this.setAttribute('fragment', text(this.buildFromStr(this.fragment)));
61
- }
62
- const that = this;
63
117
  const /** @type {AstListener} */ linkListener = (e, data) => {
64
118
  const {prevTarget} = e;
65
119
  if (prevTarget?.type === 'link-target') {
66
120
  const name = prevTarget.text(),
67
- {title, interwiki, fragment, ns, valid} = that.normalizeTitle(name);
121
+ {title, interwiki, ns, valid} = this.normalizeTitle(name);
68
122
  if (!valid) {
69
123
  undo(e, data);
70
124
  throw new Error(`非法的内链目标:${name}`);
71
- } else if (that.type === 'category' && (interwiki || ns !== 14)
72
- || that.type === 'file' && (interwiki || ns !== 6)
125
+ } else if (this.type === 'category' && (interwiki || ns !== 14)
126
+ || this.type === 'file' && (interwiki || ns !== 6)
73
127
  ) {
74
128
  undo(e, data);
75
- throw new Error(`${that.type === 'file' ? '文件' : '分类'}链接不可更改命名空间:${name}`);
76
- } else if (that.type === 'link' && !interwiki && [6, 14].includes(ns) && !name.trim().startsWith(':')) {
77
- const {firstChild} = prevTarget;
78
- if (typeof firstChild === 'string') {
79
- prevTarget.setText(`:${firstChild}`);
129
+ throw new Error(`${this.type === 'file' ? '文件' : '分类'}链接不可更改命名空间:${name}`);
130
+ } else if (this.type === 'link' && !interwiki && (ns === 6 || ns === 14) && name.trim()[0] !== ':') {
131
+ const /** @type {{firstChild: AstText}} */ {firstChild} = prevTarget;
132
+ if (firstChild.type === 'text') {
133
+ firstChild.insertData(0, ':');
80
134
  } else {
81
135
  prevTarget.prepend(':');
82
136
  }
83
137
  }
84
- that.setAttribute('selfLink', !title).setAttribute('interwiki', interwiki)
85
- .setAttribute('name', title).setAttribute('fragment', fragment);
138
+ this.setAttribute('name', title);
86
139
  }
87
140
  };
88
141
  this.addEventListener(['remove', 'insert', 'replace', 'text'], linkListener);
89
142
  return this;
90
143
  }
91
144
 
92
- toString() {
93
- const str = super.toString('|');
94
- return this.type === 'gallery-image' ? str : `[[${str}]]`;
145
+ /**
146
+ * @override
147
+ * @param {string} selector
148
+ */
149
+ toString(selector) {
150
+ const str = super.toString(selector, '|');
151
+ return this.type === 'gallery-image' || selector && this.matches(selector) ? str : `[[${str}]]`;
95
152
  }
96
153
 
154
+ /** @override */
97
155
  getPadding() {
98
156
  return 2;
99
157
  }
100
158
 
159
+ /** @override */
101
160
  getGaps() {
102
161
  return 1;
103
162
  }
104
163
 
164
+ /** @override */
105
165
  text() {
106
166
  const str = super.text('|');
107
167
  return this.type === 'gallery-image' ? str : `[[${str}]]`;
108
168
  }
109
169
 
110
- /** @param {string} link */
170
+ /**
171
+ * 设置链接目标
172
+ * @param {string} link 链接目标
173
+ * @throws `SyntaxError` 非法的链接目标
174
+ */
111
175
  setTarget(link) {
112
176
  link = String(link);
113
- if (link.type === 'link' && !/^\s*[:#]/.test(link)) {
177
+ if (this.type === 'link' && !/^\s*[:#]/u.test(link)) {
114
178
  link = `:${link}`;
115
179
  }
116
180
  const root = Parser.parse(`[[${link}]]`, this.getAttribute('include'), 6, this.getAttribute('config')),
@@ -120,23 +184,25 @@ class LinkToken extends Token {
120
184
  throw new SyntaxError(`非法的${msgs[this.type]}目标:${link}`);
121
185
  }
122
186
  const {firstChild} = firstElementChild;
123
- root.destroy();
124
- firstElementChild.destroy();
187
+ firstElementChild.destroy(true);
125
188
  this.firstElementChild.safeReplaceWith(firstChild);
126
189
  }
127
190
 
128
191
  /**
129
- * @param {string} lang
130
- * @param {string} link
192
+ * 设置跨语言链接
193
+ * @param {string} lang 语言前缀
194
+ * @param {string} link 页面标题
195
+ * @throws `SyntaxError` 非法的跨语言链接
131
196
  */
132
197
  setLangLink(lang, link) {
133
198
  if (typeof lang !== 'string') {
134
199
  this.typeError('setLangLink', 'String');
135
200
  }
136
201
  link = String(link).trim();
137
- if (link.startsWith('#')) {
202
+ const [char] = link;
203
+ if (char === '#') {
138
204
  throw new SyntaxError(`跨语言链接不能仅为fragment!`);
139
- } else if (link.startsWith(':')) {
205
+ } else if (char === ':') {
140
206
  link = link.slice(1);
141
207
  }
142
208
  const root = Parser.parse(`[[${lang}:${link}]]`, this.getAttribute('include'), 6, this.getAttribute('config')),
@@ -147,14 +213,18 @@ class LinkToken extends Token {
147
213
  throw new SyntaxError(`非法的跨语言链接目标:${lang}:${link}`);
148
214
  }
149
215
  const {firstChild} = firstElementChild;
150
- root.destroy();
151
- firstElementChild.destroy();
216
+ firstElementChild.destroy(true);
152
217
  this.firstElementChild.safeReplaceWith(firstChild);
153
218
  }
154
219
 
155
- /** @param {string} fragment */
220
+ /**
221
+ * 设置fragment
222
+ * @param {string} fragment fragment
223
+ * @param {boolean} page 是否是其他页面
224
+ * @throws `SyntaxError` 非法的fragment
225
+ */
156
226
  #setFragment(fragment, page = true) {
157
- fragment = String(fragment).replace(/[<>[]#|=!]/g, p => encodeURIComponent(p));
227
+ fragment = String(fragment).replaceAll(/[<>[\]#|=]/gu, p => encodeURIComponent(p));
158
228
  const include = this.getAttribute('include'),
159
229
  config = this.getAttribute('config'),
160
230
  root = Parser.parse(`[[${page ? `:${this.name}` : ''}#${fragment}]]`, include, 6, config),
@@ -166,16 +236,23 @@ class LinkToken extends Token {
166
236
  Parser.warn(`${this.constructor.name}.setFragment 方法会同时规范化页面名!`);
167
237
  }
168
238
  const {firstChild} = firstElementChild;
169
- root.destroy();
170
- firstElementChild.destroy();
239
+ firstElementChild.destroy(true);
171
240
  this.firstElementChild.safeReplaceWith(firstChild);
172
241
  }
173
242
 
174
- /** @param {string} fragment */
243
+ /**
244
+ * 设置fragment
245
+ * @param {string} fragment fragment
246
+ */
175
247
  setFragment(fragment) {
176
248
  this.#setFragment(fragment);
177
249
  }
178
250
 
251
+ /**
252
+ * 修改为到自身的链接
253
+ * @param {string} fragment fragment
254
+ * @throws `RangeError` 空fragment
255
+ */
179
256
  asSelfLink(fragment = this.fragment) {
180
257
  fragment = String(fragment);
181
258
  if (!fragment.trim()) {
@@ -184,6 +261,11 @@ class LinkToken extends Token {
184
261
  this.#setFragment(fragment, false);
185
262
  }
186
263
 
264
+ /**
265
+ * 设置链接显示文字
266
+ * @param {string} linkText 链接显示文字
267
+ * @throws `SyntaxError` 非法的链接显示文字
268
+ */
187
269
  setLinkText(linkText = '') {
188
270
  linkText = String(linkText);
189
271
  let lastElementChild;
@@ -208,24 +290,28 @@ class LinkToken extends Token {
208
290
  }
209
291
  }
210
292
 
293
+ /**
294
+ * 自动生成管道符后的链接文字
295
+ * @throws `Error` 带有"#"或"%"时不可用
296
+ */
211
297
  pipeTrick() {
212
298
  const linkText = this.firstElementChild.text();
213
- if (/[#%]/.test(linkText)) {
299
+ if (linkText.includes('#') || linkText.includes('%')) {
214
300
  throw new Error('Pipe trick 不能用于带有"#"或"%"的场合!');
215
301
  }
216
- const m1 = linkText.match(/^:?(?:[ \w\x80-\xff-]+:)?(.+?) ?\(.+\)$/);
302
+ const m1 = /^:?(?:[ \w\x80-\xFF-]+:)?([^(]+)\(.+\)$/u.exec(linkText);
217
303
  if (m1) {
218
- this.setLinkText(m1[1]);
304
+ this.setLinkText(m1[1].trim());
219
305
  return;
220
306
  }
221
- const m2 = linkText.match(/^:?(?:[ \w\x80-\xff-]+:)?(.+?) ?(.+)$/);
307
+ const m2 = /^:?(?:[ \w\x80-\xFF-]+:)?([^(]+)(.+)$/u.exec(linkText);
222
308
  if (m2) {
223
- this.setLinkText(m2[1]);
309
+ this.setLinkText(m2[1].trim());
224
310
  return;
225
311
  }
226
- const m3 = linkText.match(/^:?(?:[ \w\x80-\xff-]+:)?(.+?)(?: ?\(.+\))?(?:, |,|، ).+/);
312
+ const m3 = /^:?(?:[ \w\x80-\xFF-]+:)?(.+?)(?:(?<!\()\(.+\))?(?:, |,|، )./u.exec(linkText);
227
313
  if (m3) {
228
- this.setLinkText(m3[1]);
314
+ this.setLinkText(m3[1].trim());
229
315
  return;
230
316
  }
231
317
  this.setLinkText(linkText);
package/src/magicLink.js CHANGED
@@ -1,52 +1,66 @@
1
1
  'use strict';
2
2
 
3
- const /** @type {Parser} */ Parser = require('..'),
3
+ const Parser = require('..'),
4
4
  Token = require('.');
5
5
 
6
6
  /**
7
7
  * 自由外链
8
- * @classdesc `{childNodes: [...string|CommentToken|IncludeToken|NoincludeToken]}`
8
+ * @classdesc `{childNodes: [...AstText|CommentToken|IncludeToken|NoincludeToken]}`
9
9
  */
10
10
  class MagicLinkToken extends Token {
11
11
  type = 'free-ext-link';
12
12
  #protocolRegex;
13
13
 
14
+ /** 协议 */
14
15
  get protocol() {
15
- return this.text().match(this.#protocolRegex)?.[0];
16
+ return this.#protocolRegex.exec(this.text())?.[0];
16
17
  }
18
+
17
19
  set protocol(value) {
18
20
  if (typeof value !== 'string') {
19
21
  this.typeError('protocol', 'String');
20
22
  }
21
- if (!new RegExp(`${this.#protocolRegex.source}$`, 'i').test(value)) {
23
+ if (!new RegExp(`${this.#protocolRegex.source}$`, 'iu').test(value)) {
22
24
  throw new RangeError(`非法的外链协议:${value}`);
23
25
  }
24
26
  this.replaceChildren(this.text().replace(this.#protocolRegex, value));
25
27
  }
26
28
 
29
+ /** 和内链保持一致 */
30
+ get link() {
31
+ return this.text();
32
+ }
33
+
34
+ set link(url) {
35
+ this.setTarget(url);
36
+ }
37
+
27
38
  /**
28
- * @param {string} url
39
+ * @param {string} url 网址
40
+ * @param {boolean} doubleSlash 是否接受"//"作为协议
29
41
  * @param {accum} accum
30
42
  */
31
- constructor(url, doubleSlash = false, config = Parser.getConfig(), accum = []) {
43
+ constructor(url, doubleSlash, config = Parser.getConfig(), accum = []) {
32
44
  super(url, config, true, accum, {'Stage-1': ':', '!ExtToken': ''});
33
45
  if (doubleSlash) {
34
46
  this.type = 'ext-link-url';
35
47
  }
36
- this.#protocolRegex = new RegExp(`^(?:${config.protocol}${doubleSlash ? '|//' : ''})`, 'i');
48
+ this.#protocolRegex = new RegExp(`^(?:${config.protocol}${doubleSlash ? '|//' : ''})`, 'iu');
37
49
  }
38
50
 
51
+ /** @override */
39
52
  afterBuild() {
40
- const ParameterToken = require('./parameter'),
41
- /** @type {ParameterToken} */ parameter = this.closest('parameter');
53
+ const ParameterToken = require('./parameter');
54
+ const /** @type {ParameterToken} */ parameter = this.closest('parameter');
42
55
  if (parameter?.getValue() === this.text()) {
43
- this.replaceWith(this.toString());
56
+ this.replaceWith(...this.childNodes);
44
57
  }
45
58
  return this;
46
59
  }
47
60
 
61
+ /** @override */
48
62
  cloneNode() {
49
- const cloned = this.cloneChildren(),
63
+ const cloned = this.cloneChildNodes(),
50
64
  token = Parser.run(() => new MagicLinkToken(
51
65
  undefined, this.type === 'ext-link-url', this.getAttribute('config'),
52
66
  ));
@@ -55,6 +69,10 @@ class MagicLinkToken extends Token {
55
69
  return token;
56
70
  }
57
71
 
72
+ /**
73
+ * 获取网址
74
+ * @throws `Error` 非标准协议
75
+ */
58
76
  getUrl() {
59
77
  let url = this.text();
60
78
  if (url.startsWith('//')) {
@@ -70,7 +88,11 @@ class MagicLinkToken extends Token {
70
88
  }
71
89
  }
72
90
 
73
- /** @param {string|URL} url */
91
+ /**
92
+ * 设置外链目标
93
+ * @param {string|URL} url 含协议的网址
94
+ * @throws `SyntaxError` 非法的自由外链目标
95
+ */
74
96
  setTarget(url) {
75
97
  url = String(url);
76
98
  const root = Parser.parse(url, this.getAttribute('include'), 9, this.getAttribute('config')),
@@ -1,41 +1,53 @@
1
1
  'use strict';
2
2
 
3
3
  const hidden = require('../../mixin/hidden'),
4
- /** @type {Parser} */ Parser = require('../..'),
4
+ Parser = require('../..'),
5
5
  NowikiToken = require('.');
6
6
 
7
7
  /**
8
8
  * HTML注释,不可见
9
- * @classdesc `{childNodes: [string]}`
9
+ * @classdesc `{childNodes: [AstText]}`
10
10
  */
11
11
  class CommentToken extends hidden(NowikiToken) {
12
12
  type = 'comment';
13
13
  closed;
14
14
 
15
+ /** 内部wikitext */
16
+ get innerText() {
17
+ return String(this.firstChild);
18
+ }
19
+
15
20
  /**
16
- * @param {string} wikitext
21
+ * @param {string} wikitext wikitext
22
+ * @param {boolean} closed 是否闭合
17
23
  * @param {accum} accum
18
24
  */
19
25
  constructor(wikitext, closed = true, config = Parser.getConfig(), accum = []) {
20
26
  super(wikitext, config, accum);
21
27
  this.closed = closed;
28
+ Object.defineProperty(this, 'closed', {enumerable: false});
22
29
  }
23
30
 
24
- /** @this {CommentToken & {firstChild: string}} */
31
+ /** @override */
25
32
  cloneNode() {
26
- return Parser.run(() => new CommentToken(this.firstChild, this.closed, this.getAttribute('config')));
33
+ return Parser.run(() => new CommentToken(String(this.firstChild), this.closed, this.getAttribute('config')));
27
34
  }
28
35
 
29
- /** @this {CommentToken & {firstChild: string}} */
30
- toString() {
31
- const {firstChild, closed, nextSibling} = this;
32
- if (!closed && nextSibling) {
36
+ /**
37
+ * @override
38
+ * @param {string} selector
39
+ */
40
+ toString(selector) {
41
+ if (!this.closed && this.nextSibling) {
33
42
  Parser.error('自动闭合HTML注释', this);
34
43
  this.closed = true;
35
44
  }
36
- return `<!--${firstChild}${this.closed ? '-->' : ''}`;
45
+ return selector && this.matches(selector)
46
+ ? ''
47
+ : `<!--${String(this.firstChild)}${this.closed ? '-->' : ''}`;
37
48
  }
38
49
 
50
+ /** @override */
39
51
  getPadding() {
40
52
  return 4;
41
53
  }
package/src/nowiki/dd.js CHANGED
@@ -1,41 +1,56 @@
1
1
  'use strict';
2
2
 
3
- const /** @type {Parser} */ Parser = require('../..'),
3
+ const Parser = require('../..'),
4
4
  NowikiToken = require('.');
5
5
 
6
6
  /**
7
7
  * :
8
- * @classdesc `{childNodes: [string]}`
8
+ * @classdesc `{childNodes: [AstText]}`
9
9
  */
10
10
  class DdToken extends NowikiToken {
11
11
  type = 'dd';
12
- dt = false;
13
- ul = false;
14
- ol = false;
15
- indent = 0;
16
-
17
- /** @param {string} str */
18
- #update(str) {
19
- this.setAttribute('ul', str.includes('*')).setAttribute('ol', str.includes('#'))
20
- .setAttribute('dt', str.includes(';')).setAttribute('indent', str.split(':').length - 1);
12
+
13
+ /** 是否包含<dt> */
14
+ get dt() {
15
+ return String(this).includes(';');
21
16
  }
22
17
 
23
- /**
24
- * @param {string} str
25
- * @param {accum} accum
26
- */
27
- constructor(str, config = Parser.getConfig(), accum = []) {
28
- super(str, config, accum);
29
- this.seal(['dt', 'ul', 'ol', 'indent']).#update(str);
18
+ /** 是否包含<ul> */
19
+ get ul() {
20
+ return String(this).includes('*');
21
+ }
22
+
23
+ /** 是否包含<ol> */
24
+ get ol() {
25
+ return String(this).includes('#');
26
+ }
27
+
28
+ /** 缩进数 */
29
+ get indent() {
30
+ return String(this).split(':').length - 1;
31
+ }
32
+
33
+ set indent(indent) {
34
+ if (this.type === 'dd') {
35
+ if (typeof indent !== 'number') {
36
+ this.typeError('set indent', 'Number');
37
+ } else if (!Number.isInteger(indent) || indent < 0) {
38
+ throw new RangeError(`indent 应为自然数!${indent}`);
39
+ }
40
+ this.setText(':'.repeat(indent));
41
+ }
30
42
  }
31
43
 
32
- /** @param {string} str */
44
+ /**
45
+ * @override
46
+ * @param {string} str 新文本
47
+ * @throws `RangeError` 错误的列表语法
48
+ */
33
49
  setText(str) {
34
50
  const src = this.type === 'dd' ? ':' : ';:*#';
35
- if (new RegExp(`[^${src}]`).test(str)) {
36
- throw new RangeError(`${this.constructor.name} 仅能包含${src.split('').map(c => `"${c}"`).join('、')}!`);
51
+ if (new RegExp(`[^${src}]`, 'u').test(str)) {
52
+ throw new RangeError(`${this.constructor.name} 仅能包含${[...src].map(c => `"${c}"`).join('、')}!`);
37
53
  }
38
- this.#update(str);
39
54
  return super.setText(str);
40
55
  }
41
56
  }
@@ -1,18 +1,18 @@
1
1
  'use strict';
2
2
 
3
3
  const hidden = require('../../mixin/hidden'),
4
- /** @type {Parser} */ Parser = require('../..'),
4
+ Parser = require('../..'),
5
5
  NowikiToken = require('.');
6
6
 
7
7
  /**
8
8
  * 状态开关
9
- * @classdesc `{childNodes: [string]}`
9
+ * @classdesc `{childNodes: [AstText]}`
10
10
  */
11
11
  class DoubleUnderscoreToken extends hidden(NowikiToken) {
12
12
  type = 'double-underscore';
13
13
 
14
14
  /**
15
- * @param {string} word
15
+ * @param {string} word 状态开关名
16
16
  * @param {accum} accum
17
17
  */
18
18
  constructor(word, config = Parser.getConfig(), accum = []) {
@@ -20,19 +20,28 @@ class DoubleUnderscoreToken extends hidden(NowikiToken) {
20
20
  this.setAttribute('name', word.toLowerCase());
21
21
  }
22
22
 
23
+ /** @override */
23
24
  cloneNode() {
24
- return Parser.run(() => new DoubleUnderscoreToken(this.firstChild, this.getAttribute('config')));
25
+ return Parser.run(() => new DoubleUnderscoreToken(String(this.firstChild), this.getAttribute('config')));
25
26
  }
26
27
 
27
- /** @this {DoubleUnderscoreToken & {firstChild: string}} */
28
- toString() {
29
- return `__${this.firstChild}__`;
28
+ /**
29
+ * @override
30
+ * @param {string} selector
31
+ */
32
+ toString(selector) {
33
+ return selector && this.matches(selector) ? '' : `__${String(this.firstChild)}__`;
30
34
  }
31
35
 
36
+ /** @override */
32
37
  getPadding() {
33
38
  return 2;
34
39
  }
35
40
 
41
+ /**
42
+ * @override
43
+ * @throws `Error` 禁止修改
44
+ */
36
45
  setText() {
37
46
  throw new Error(`禁止修改 ${this.constructor.name}!`);
38
47
  }
package/src/nowiki/hr.js CHANGED
@@ -1,32 +1,36 @@
1
1
  'use strict';
2
2
 
3
3
  const sol = require('../../mixin/sol'),
4
- /** @type {Parser} */ Parser = require('../..'),
4
+ Parser = require('../..'),
5
5
  NowikiToken = require('.');
6
6
 
7
7
  /**
8
8
  * `<hr>`
9
- * @classdesc `{childNodes: [string]}`
9
+ * @classdesc `{childNodes: [AstText]}`
10
10
  */
11
11
  class HrToken extends sol(NowikiToken) {
12
12
  type = 'hr';
13
13
 
14
14
  /**
15
- * @param {number} n
15
+ * @param {number} n 字符串长度
16
16
  * @param {accum} accum
17
17
  */
18
18
  constructor(n, config = Parser.getConfig(), accum = []) {
19
19
  super('-'.repeat(n), config, accum);
20
20
  }
21
21
 
22
- /** @this {HrToken & {firstChild: string}} */
22
+ /** @override */
23
23
  cloneNode() {
24
- return Parser.run(() => new HrToken(this.firstChild.length, this.getAttribute('config')));
24
+ return Parser.run(() => new HrToken(String(this).length, this.getAttribute('config')));
25
25
  }
26
26
 
27
- /** @param {string} str */
27
+ /**
28
+ * @override
29
+ * @param {string} str 新文本
30
+ * @throws `RangeError` 错误的\<hr\>语法
31
+ */
28
32
  setText(str) {
29
- if (!/^-{4,}$/.test(str)) {
33
+ if (str.length < 4 || /[^-]/u.test(str)) {
30
34
  throw new RangeError('<hr>总是写作不少于4个的连续"-"!');
31
35
  }
32
36
  return super.setText(str);