wikiparser-node 0.11.0 → 1.0.0-beta.1

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 (252) hide show
  1. package/config/.schema.json +26 -0
  2. package/dist/index.d.ts +61 -113
  3. package/dist/index.js +314 -0
  4. package/dist/lib/element.d.ts +122 -108
  5. package/dist/lib/element.js +656 -0
  6. package/dist/lib/node.d.ts +115 -221
  7. package/dist/lib/node.js +473 -0
  8. package/dist/lib/ranges.d.ts +27 -26
  9. package/dist/lib/ranges.js +130 -0
  10. package/dist/lib/text.d.ts +36 -28
  11. package/dist/lib/text.js +215 -0
  12. package/dist/lib/title.d.ts +26 -12
  13. package/dist/lib/title.js +108 -0
  14. package/dist/mixin/attributesParent.js +90 -0
  15. package/dist/mixin/fixed.js +29 -0
  16. package/dist/mixin/hidden.js +19 -0
  17. package/dist/mixin/singleLine.js +23 -0
  18. package/dist/mixin/sol.js +41 -0
  19. package/dist/parser/brackets.js +118 -0
  20. package/dist/parser/commentAndExt.js +48 -0
  21. package/dist/parser/converter.js +31 -0
  22. package/dist/parser/externalLinks.js +22 -0
  23. package/dist/parser/hrAndDoubleUnderscore.js +35 -0
  24. package/dist/parser/html.js +29 -0
  25. package/dist/parser/links.js +86 -0
  26. package/dist/parser/list.js +51 -0
  27. package/dist/parser/magicLinks.js +32 -0
  28. package/dist/parser/quotes.js +57 -0
  29. package/dist/parser/selector.js +158 -0
  30. package/dist/parser/table.js +108 -0
  31. package/dist/src/arg.d.ts +47 -23
  32. package/dist/src/arg.js +196 -0
  33. package/dist/src/atom.d.ts +12 -0
  34. package/dist/src/atom.js +22 -0
  35. package/dist/src/attribute.d.ts +74 -33
  36. package/dist/src/attribute.js +433 -0
  37. package/dist/src/attributes.d.ts +61 -55
  38. package/dist/src/attributes.js +371 -0
  39. package/dist/src/converter.d.ts +45 -71
  40. package/dist/src/converter.js +135 -0
  41. package/dist/src/converterFlags.d.ts +64 -39
  42. package/dist/src/converterFlags.js +235 -0
  43. package/dist/src/converterRule.d.ts +49 -27
  44. package/dist/src/converterRule.js +255 -0
  45. package/dist/src/extLink.d.ts +41 -38
  46. package/dist/src/extLink.js +154 -0
  47. package/dist/src/gallery.d.ts +59 -18
  48. package/dist/src/gallery.js +132 -0
  49. package/dist/src/heading.d.ts +60 -22
  50. package/dist/src/heading.js +135 -0
  51. package/dist/src/hidden.d.ts +20 -0
  52. package/dist/src/hidden.js +24 -0
  53. package/dist/src/html.d.ts +83 -29
  54. package/dist/src/html.js +242 -0
  55. package/dist/src/imageParameter.d.ts +59 -40
  56. package/dist/src/imageParameter.js +251 -0
  57. package/dist/src/imagemap.d.ts +65 -21
  58. package/dist/src/imagemap.js +169 -0
  59. package/dist/src/imagemapLink.d.ts +46 -14
  60. package/dist/src/imagemapLink.js +38 -0
  61. package/dist/src/index.d.ts +71 -105
  62. package/dist/src/index.js +826 -0
  63. package/dist/src/link/base.d.ts +71 -0
  64. package/dist/src/link/base.js +225 -0
  65. package/dist/src/link/category.d.ts +10 -11
  66. package/dist/src/link/category.js +26 -0
  67. package/dist/src/link/file.d.ts +61 -39
  68. package/dist/src/link/file.js +242 -0
  69. package/dist/src/link/galleryImage.d.ts +34 -12
  70. package/dist/src/link/galleryImage.js +98 -0
  71. package/dist/src/link/index.d.ts +25 -63
  72. package/dist/src/link/index.js +136 -0
  73. package/dist/src/magicLink.d.ts +22 -15
  74. package/dist/src/magicLink.js +126 -0
  75. package/dist/src/nested.d.ts +47 -0
  76. package/dist/src/nested.js +84 -0
  77. package/dist/src/nowiki/base.d.ts +39 -0
  78. package/dist/src/nowiki/base.js +29 -0
  79. package/dist/src/nowiki/comment.d.ts +31 -20
  80. package/dist/src/nowiki/comment.js +61 -0
  81. package/dist/src/nowiki/dd.d.ts +17 -11
  82. package/dist/src/nowiki/dd.js +50 -0
  83. package/dist/src/nowiki/doubleUnderscore.d.ts +28 -13
  84. package/dist/src/nowiki/doubleUnderscore.js +45 -0
  85. package/dist/src/nowiki/hr.d.ts +28 -10
  86. package/dist/src/nowiki/hr.js +33 -0
  87. package/dist/src/nowiki/index.d.ts +17 -23
  88. package/dist/src/nowiki/index.js +21 -0
  89. package/dist/src/nowiki/list.d.ts +15 -7
  90. package/dist/src/nowiki/list.js +11 -0
  91. package/dist/src/nowiki/noinclude.d.ts +20 -7
  92. package/dist/src/nowiki/noinclude.js +22 -0
  93. package/dist/src/nowiki/quote.d.ts +25 -10
  94. package/dist/src/nowiki/quote.js +55 -0
  95. package/dist/src/onlyinclude.d.ts +28 -12
  96. package/dist/src/onlyinclude.js +64 -0
  97. package/dist/src/paramTag/index.d.ts +40 -17
  98. package/dist/src/paramTag/index.js +76 -0
  99. package/dist/src/paramTag/inputbox.d.ts +8 -7
  100. package/dist/src/paramTag/inputbox.js +19 -0
  101. package/dist/src/parameter.d.ts +62 -41
  102. package/dist/src/parameter.js +201 -0
  103. package/dist/src/pre.d.ts +32 -0
  104. package/dist/src/pre.js +39 -0
  105. package/dist/src/syntax.d.ts +17 -14
  106. package/dist/src/syntax.js +65 -0
  107. package/dist/src/table/base.d.ts +55 -0
  108. package/dist/src/table/base.js +77 -0
  109. package/dist/src/table/index.d.ts +123 -156
  110. package/dist/src/table/index.js +811 -0
  111. package/dist/src/table/td.d.ts +90 -67
  112. package/dist/src/table/td.js +276 -0
  113. package/dist/src/table/tr.d.ts +30 -85
  114. package/dist/src/table/tr.js +48 -0
  115. package/dist/src/table/trBase.d.ts +72 -0
  116. package/dist/src/table/trBase.js +153 -0
  117. package/dist/src/tagPair/ext.d.ts +47 -11
  118. package/dist/src/tagPair/ext.js +127 -0
  119. package/dist/src/tagPair/include.d.ts +32 -13
  120. package/dist/src/tagPair/include.js +40 -0
  121. package/dist/src/tagPair/index.d.ts +44 -29
  122. package/dist/src/tagPair/index.js +111 -0
  123. package/dist/src/transclude.d.ts +88 -85
  124. package/dist/src/transclude.js +739 -0
  125. package/dist/util/base.js +26 -0
  126. package/dist/util/debug.js +52 -0
  127. package/dist/util/diff.js +69 -0
  128. package/dist/util/lint.js +38 -0
  129. package/dist/util/string.js +103 -0
  130. package/errors/README +1 -0
  131. package/i18n/zh-hans.json +1 -0
  132. package/i18n/zh-hant.json +1 -0
  133. package/package.json +21 -24
  134. package/printed/README +1 -0
  135. package/dist/mixin/attributeParent.d.ts +0 -9
  136. package/dist/mixin/fixedToken.d.ts +0 -8
  137. package/dist/mixin/hidden.d.ts +0 -8
  138. package/dist/mixin/singleLine.d.ts +0 -8
  139. package/dist/mixin/sol.d.ts +0 -8
  140. package/dist/parser/brackets.d.ts +0 -12
  141. package/dist/parser/commentAndExt.d.ts +0 -8
  142. package/dist/parser/converter.d.ts +0 -7
  143. package/dist/parser/externalLinks.d.ts +0 -7
  144. package/dist/parser/hrAndDoubleUnderscore.d.ts +0 -11
  145. package/dist/parser/html.d.ts +0 -7
  146. package/dist/parser/links.d.ts +0 -7
  147. package/dist/parser/list.d.ts +0 -7
  148. package/dist/parser/magicLinks.d.ts +0 -7
  149. package/dist/parser/quotes.d.ts +0 -7
  150. package/dist/parser/selector.d.ts +0 -12
  151. package/dist/parser/table.d.ts +0 -11
  152. package/dist/src/atom/hidden.d.ts +0 -5
  153. package/dist/src/atom/index.d.ts +0 -15
  154. package/dist/src/charinsert.d.ts +0 -32
  155. package/dist/src/hasNowiki/index.d.ts +0 -14
  156. package/dist/src/hasNowiki/pre.d.ts +0 -13
  157. package/dist/src/nested/choose.d.ts +0 -13
  158. package/dist/src/nested/combobox.d.ts +0 -13
  159. package/dist/src/nested/index.d.ts +0 -18
  160. package/dist/src/nested/references.d.ts +0 -13
  161. package/dist/tool/index.d.ts +0 -420
  162. package/dist/util/base.d.ts +0 -10
  163. package/dist/util/debug.d.ts +0 -20
  164. package/dist/util/diff.d.ts +0 -8
  165. package/dist/util/lint.d.ts +0 -28
  166. package/dist/util/string.d.ts +0 -55
  167. package/index.js +0 -333
  168. package/lib/element.js +0 -618
  169. package/lib/node.js +0 -730
  170. package/lib/ranges.js +0 -130
  171. package/lib/text.js +0 -265
  172. package/lib/title.js +0 -83
  173. package/mixin/attributeParent.js +0 -117
  174. package/mixin/fixedToken.js +0 -40
  175. package/mixin/hidden.js +0 -21
  176. package/mixin/singleLine.js +0 -31
  177. package/mixin/sol.js +0 -54
  178. package/parser/brackets.js +0 -128
  179. package/parser/commentAndExt.js +0 -62
  180. package/parser/converter.js +0 -46
  181. package/parser/externalLinks.js +0 -33
  182. package/parser/hrAndDoubleUnderscore.js +0 -49
  183. package/parser/html.js +0 -42
  184. package/parser/links.js +0 -94
  185. package/parser/list.js +0 -59
  186. package/parser/magicLinks.js +0 -41
  187. package/parser/quotes.js +0 -64
  188. package/parser/selector.js +0 -180
  189. package/parser/table.js +0 -114
  190. package/src/arg.js +0 -207
  191. package/src/atom/hidden.js +0 -13
  192. package/src/atom/index.js +0 -43
  193. package/src/attribute.js +0 -472
  194. package/src/attributes.js +0 -453
  195. package/src/charinsert.js +0 -97
  196. package/src/converter.js +0 -176
  197. package/src/converterFlags.js +0 -284
  198. package/src/converterRule.js +0 -256
  199. package/src/extLink.js +0 -180
  200. package/src/gallery.js +0 -149
  201. package/src/hasNowiki/index.js +0 -44
  202. package/src/hasNowiki/pre.js +0 -40
  203. package/src/heading.js +0 -134
  204. package/src/html.js +0 -254
  205. package/src/imageParameter.js +0 -303
  206. package/src/imagemap.js +0 -199
  207. package/src/imagemapLink.js +0 -41
  208. package/src/index.js +0 -938
  209. package/src/link/category.js +0 -44
  210. package/src/link/file.js +0 -287
  211. package/src/link/galleryImage.js +0 -120
  212. package/src/link/index.js +0 -388
  213. package/src/magicLink.js +0 -151
  214. package/src/nested/choose.js +0 -24
  215. package/src/nested/combobox.js +0 -23
  216. package/src/nested/index.js +0 -96
  217. package/src/nested/references.js +0 -23
  218. package/src/nowiki/comment.js +0 -71
  219. package/src/nowiki/dd.js +0 -59
  220. package/src/nowiki/doubleUnderscore.js +0 -56
  221. package/src/nowiki/hr.js +0 -41
  222. package/src/nowiki/index.js +0 -56
  223. package/src/nowiki/list.js +0 -16
  224. package/src/nowiki/noinclude.js +0 -28
  225. package/src/nowiki/quote.js +0 -69
  226. package/src/onlyinclude.js +0 -64
  227. package/src/paramTag/index.js +0 -89
  228. package/src/paramTag/inputbox.js +0 -35
  229. package/src/parameter.js +0 -239
  230. package/src/syntax.js +0 -91
  231. package/src/table/index.js +0 -985
  232. package/src/table/td.js +0 -343
  233. package/src/table/tr.js +0 -319
  234. package/src/tagPair/ext.js +0 -146
  235. package/src/tagPair/include.js +0 -50
  236. package/src/tagPair/index.js +0 -131
  237. package/src/transclude.js +0 -843
  238. package/tool/index.js +0 -1209
  239. package/typings/api.d.ts +0 -9
  240. package/typings/array.d.ts +0 -29
  241. package/typings/event.d.ts +0 -22
  242. package/typings/index.d.ts +0 -118
  243. package/typings/node.d.ts +0 -35
  244. package/typings/parser.d.ts +0 -12
  245. package/typings/table.d.ts +0 -10
  246. package/typings/token.d.ts +0 -31
  247. package/typings/tool.d.ts +0 -6
  248. package/util/base.js +0 -17
  249. package/util/debug.js +0 -73
  250. package/util/diff.js +0 -76
  251. package/util/lint.js +0 -57
  252. package/util/string.js +0 -126
@@ -0,0 +1,656 @@
1
+ "use strict";
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const string_1 = require("../util/string");
5
+ const Ranges = require("./ranges");
6
+ const { nth } = Ranges;
7
+ const parseSelector = require("../parser/selector");
8
+ const Title = require("./title");
9
+ const Parser = require("../index");
10
+ const AstNode = require("./node");
11
+ const lintIgnoredExt = new Set([
12
+ 'nowiki',
13
+ 'pre',
14
+ 'charinsert',
15
+ 'score',
16
+ 'syntaxhighlight',
17
+ 'source',
18
+ 'math',
19
+ 'chem',
20
+ 'ce',
21
+ 'graph',
22
+ 'mapframe',
23
+ 'maplink',
24
+ 'quiz',
25
+ 'templatedata',
26
+ 'timeline',
27
+ ]);
28
+ /** 类似HTMLElement */
29
+ class AstElement extends AstNode {
30
+ /** @browser */
31
+ name;
32
+ /**
33
+ * 子节点总数
34
+ * @browser
35
+ */
36
+ get length() {
37
+ return this.childNodes.length;
38
+ }
39
+ /** 全部非文本子节点 */
40
+ get children() {
41
+ return this.childNodes.filter(({ type }) => type !== 'text');
42
+ }
43
+ /** 首位非文本子节点 */
44
+ get firstElementChild() {
45
+ return this.childNodes.find(({ type }) => type !== 'text');
46
+ }
47
+ /** 末位非文本子节点 */
48
+ get lastElementChild() {
49
+ return this.children.at(-1);
50
+ }
51
+ /** 非文本子节点总数 */
52
+ get childElementCount() {
53
+ return this.children.length;
54
+ }
55
+ /** 父节点 */
56
+ get parentElement() {
57
+ return this.parentNode;
58
+ }
59
+ /** AstElement.prototype.text()的getter写法 */
60
+ get outerText() {
61
+ return this.text();
62
+ }
63
+ /** 不可见 */
64
+ get hidden() {
65
+ return this.text() === '';
66
+ }
67
+ /** 后一个可见的兄弟节点 */
68
+ get nextVisibleSibling() {
69
+ let { nextSibling } = this;
70
+ while (nextSibling?.text() === '') {
71
+ ({ nextSibling } = nextSibling);
72
+ }
73
+ return nextSibling;
74
+ }
75
+ /** 前一个可见的兄弟节点 */
76
+ get previousVisibleSibling() {
77
+ let { previousSibling } = this;
78
+ while (previousSibling?.text() === '') {
79
+ ({ previousSibling } = previousSibling);
80
+ }
81
+ return previousSibling;
82
+ }
83
+ /** 内部高度 */
84
+ get clientHeight() {
85
+ const { innerText } = this;
86
+ return typeof innerText === 'string' ? innerText.split('\n').length : undefined;
87
+ }
88
+ /** 内部宽度 */
89
+ get clientWidth() {
90
+ const { innerText } = this;
91
+ return typeof innerText === 'string' ? innerText.split('\n').at(-1).length : undefined;
92
+ }
93
+ constructor() {
94
+ super();
95
+ this.seal('name');
96
+ }
97
+ /**
98
+ * 可见部分
99
+ * @browser
100
+ * @param separator 子节点间的连接符
101
+ */
102
+ text(separator = '') {
103
+ return (0, string_1.text)(this.childNodes, separator);
104
+ }
105
+ /**
106
+ * 合并相邻的文本子节点
107
+ * @browser
108
+ */
109
+ normalize() {
110
+ const childNodes = [...this.childNodes];
111
+ for (let i = childNodes.length - 1; i >= 0; i--) {
112
+ const cur = childNodes[i], prev = childNodes[i - 1];
113
+ if (cur.type !== 'text' || this.getGaps(i - 1)) {
114
+ //
115
+ }
116
+ else if (cur.data === '') {
117
+ childNodes.splice(i, 1);
118
+ }
119
+ else if (prev?.type === 'text') {
120
+ prev.setAttribute('data', prev.data + cur.data);
121
+ childNodes.splice(i, 1);
122
+ }
123
+ }
124
+ this.setAttribute('childNodes', childNodes);
125
+ }
126
+ /**
127
+ * 移除子节点
128
+ * @browser
129
+ * @param i 移除位置
130
+ */
131
+ removeAt(i) {
132
+ this.verifyChild(i);
133
+ const childNodes = [...this.childNodes], e = new Event('remove', { bubbles: true }), [node] = childNodes.splice(i, 1);
134
+ node.setAttribute('parentNode', undefined);
135
+ this.setAttribute('childNodes', childNodes);
136
+ this.dispatchEvent(e, { position: i, removed: node });
137
+ return node;
138
+ }
139
+ /**
140
+ * 插入子节点
141
+ * @browser
142
+ * @param node 待插入的子节点
143
+ * @param i 插入位置
144
+ * @throws `RangeError` 不能插入祖先节点
145
+ */
146
+ insertAt(node, i = this.childNodes.length) {
147
+ if (!(node instanceof AstNode)) {
148
+ return this.typeError('insertAt', 'AstNode');
149
+ }
150
+ else if (node.contains(this)) {
151
+ Parser.error('不能插入祖先节点!', node);
152
+ throw new RangeError('不能插入祖先节点!');
153
+ }
154
+ this.verifyChild(i, 1);
155
+ const childNodes = [...this.childNodes], e = new Event('insert', { bubbles: true }), j = Parser.running ? -1 : childNodes.indexOf(node);
156
+ if (j === -1) {
157
+ node.parentNode?.removeChild(node);
158
+ node.setAttribute('parentNode', this);
159
+ }
160
+ else {
161
+ childNodes.splice(j, 1);
162
+ }
163
+ childNodes.splice(i, 0, node);
164
+ this.setAttribute('childNodes', childNodes);
165
+ this.dispatchEvent(e, { position: i < 0 ? i + this.childNodes.length - 1 : i, inserted: node });
166
+ return node;
167
+ }
168
+ /**
169
+ * 最近的祖先节点
170
+ * @browser
171
+ */
172
+ closest(selector) {
173
+ let { parentNode } = this;
174
+ const stack = parseSelector(selector);
175
+ while (parentNode) {
176
+ if (parentNode.#matchesStack(stack)) {
177
+ return parentNode;
178
+ }
179
+ ({ parentNode } = parentNode);
180
+ }
181
+ return undefined;
182
+ }
183
+ /**
184
+ * 在末尾批量插入子节点
185
+ * @browser
186
+ * @param elements 插入节点
187
+ */
188
+ append(...elements) {
189
+ for (const element of elements) {
190
+ this.insertAt(element);
191
+ }
192
+ }
193
+ /**
194
+ * 批量替换子节点
195
+ * @browser
196
+ * @param elements 新的子节点
197
+ */
198
+ replaceChildren(...elements) {
199
+ for (let i = this.length - 1; i >= 0; i--) {
200
+ this.removeAt(i);
201
+ }
202
+ this.append(...elements);
203
+ }
204
+ /**
205
+ * 修改文本子节点
206
+ * @browser
207
+ * @param str 新文本
208
+ * @param i 子节点位置
209
+ * @throws `RangeError` 对应位置的子节点不是文本节点
210
+ */
211
+ setText(str, i = 0) {
212
+ this.verifyChild(i);
213
+ const oldText = this.childNodes.at(i), { type, constructor: { name } } = oldText;
214
+ if (type === 'text') {
215
+ const { data } = oldText;
216
+ oldText.replaceData(str);
217
+ return data;
218
+ }
219
+ throw new RangeError(`第 ${i} 个子节点是 ${name}!`);
220
+ }
221
+ /**
222
+ * 还原为wikitext
223
+ * @browser
224
+ * @param separator 子节点间的连接符
225
+ */
226
+ toString(selector, separator = '') {
227
+ return selector && this.matches(selector)
228
+ ? ''
229
+ : this.childNodes.map(child => child.toString(selector)).join(separator);
230
+ }
231
+ /**
232
+ * Linter
233
+ * @browser
234
+ */
235
+ lint(start = this.getAbsoluteIndex()) {
236
+ const SyntaxToken = require('../src/syntax');
237
+ if (this instanceof SyntaxToken || this.constructor.hidden
238
+ || this.type === 'ext-inner' && lintIgnoredExt.has(this.name)) {
239
+ return [];
240
+ }
241
+ const errors = [];
242
+ for (let i = 0, cur = start + this.getPadding(); i < this.length; i++) {
243
+ const child = this.childNodes[i];
244
+ errors.push(...child.lint(cur));
245
+ cur += String(child).length + this.getGaps(i);
246
+ }
247
+ return errors;
248
+ }
249
+ /**
250
+ * 以HTML格式打印
251
+ * @browser
252
+ * @param opt 选项
253
+ */
254
+ print(opt = {}) {
255
+ return String(this)
256
+ ? `<span class="wpb-${opt.class ?? this.type}">${(0, string_1.print)(this.childNodes, opt)}</span>`
257
+ : '';
258
+ }
259
+ /**
260
+ * 保存为JSON
261
+ * @browser
262
+ * @param file 文件名
263
+ */
264
+ json(file) {
265
+ const json = {
266
+ ...this,
267
+ childNodes: this.childNodes.map(child => child.type === 'text' ? String(child) : child.json()),
268
+ };
269
+ if (typeof file === 'string') {
270
+ fs.writeFileSync(path.join(__dirname.slice(0, -4), '..', 'printed', `${file}${file.endsWith('.json') ? '' : '.json'}`), JSON.stringify(json, null, 2));
271
+ }
272
+ return json;
273
+ }
274
+ /** 销毁 */
275
+ destroy() {
276
+ this.parentNode?.destroy();
277
+ for (const child of this.childNodes) {
278
+ child.setAttribute('parentNode', undefined);
279
+ }
280
+ Object.setPrototypeOf(this, null);
281
+ }
282
+ /** 是否受保护。保护条件来自Token,这里仅提前用于:required和:optional伪选择器。 */
283
+ #isProtected() {
284
+ const { parentNode } = this;
285
+ if (!parentNode) {
286
+ return undefined;
287
+ }
288
+ const { childNodes, fixed } = parentNode, protectedIndices = parentNode.getAttribute('protectedChildren').applyTo(childNodes);
289
+ return fixed || protectedIndices.includes(childNodes.indexOf(this));
290
+ }
291
+ /** @private */
292
+ matchesAttr(key, equal, val = '', i) {
293
+ if (!equal) {
294
+ return this.hasAttribute(key);
295
+ }
296
+ else if (!this.hasAttribute(key)) {
297
+ return equal === '!=';
298
+ }
299
+ const v = (0, string_1.toCase)(val, i);
300
+ let thisVal = this.getAttribute(key);
301
+ if (thisVal instanceof RegExp) {
302
+ thisVal = thisVal.source;
303
+ }
304
+ if (equal === '~=') {
305
+ const thisVals = typeof thisVal === 'string' ? thisVal.split(/\s/u) : thisVal;
306
+ return Boolean(thisVals?.[Symbol.iterator])
307
+ && [...thisVals].some(w => typeof w === 'string' && (0, string_1.toCase)(w, i) === v);
308
+ }
309
+ else if (typeof thisVal !== 'string') {
310
+ throw new RangeError(`复杂属性 ${key} 不能用于选择器!`);
311
+ }
312
+ const stringVal = (0, string_1.toCase)(thisVal, i);
313
+ switch (equal) {
314
+ case '|=':
315
+ return stringVal === v || stringVal.startsWith(`${v}-`);
316
+ case '^=':
317
+ return stringVal.startsWith(v);
318
+ case '$=':
319
+ return stringVal.endsWith(v);
320
+ case '*=':
321
+ return stringVal.includes(v);
322
+ case '!=':
323
+ return stringVal !== v;
324
+ default: // `=`
325
+ return stringVal === v;
326
+ }
327
+ }
328
+ /**
329
+ * 检查是否符合解析后的选择器,不含节点关系
330
+ * @param step 解析后的选择器
331
+ * @throws `SyntaxError` 未定义的伪选择器
332
+ */
333
+ #matches(step) {
334
+ const { parentNode, type, name, childNodes, link, fixed, constructor: { name: tokenName }, } = this, children = parentNode?.children, childrenOfType = children?.filter(({ type: t }) => t === type), siblingsCount = children?.length ?? 1, siblingsCountOfType = childrenOfType?.length ?? 1, index = (children?.indexOf(this) ?? 0) + 1, indexOfType = (childrenOfType?.indexOf(this) ?? 0) + 1, lastIndex = siblingsCount - index + 1, lastIndexOfType = siblingsCountOfType - indexOfType + 1;
335
+ return step.every(selector => {
336
+ if (typeof selector === 'string') {
337
+ switch (selector) { // 情形1:简单伪选择器、type和name
338
+ case '*':
339
+ return true;
340
+ case ':root':
341
+ return !parentNode;
342
+ case ':first-child':
343
+ return index === 1;
344
+ case ':first-of-type':
345
+ return indexOfType === 1;
346
+ case ':last-child':
347
+ return lastIndex === 1;
348
+ case ':last-of-type':
349
+ return lastIndexOfType === 1;
350
+ case ':only-child':
351
+ return siblingsCount === 1;
352
+ case ':only-of-type':
353
+ return siblingsCountOfType === 1;
354
+ case ':empty':
355
+ return !childNodes.some(child => child.type !== 'text' || String(child));
356
+ case ':parent':
357
+ return childNodes.some(child => child.type !== 'text' || String(child));
358
+ case ':header':
359
+ return type === 'heading';
360
+ case ':hidden':
361
+ return this.text() === '';
362
+ case ':visible':
363
+ return this.text() !== '';
364
+ case ':only-whitespace':
365
+ return this.text().trim() === '';
366
+ case ':any-link':
367
+ return type === 'link' || type === 'free-ext-link' || type === 'ext-link'
368
+ || (type === 'file' || type === 'gallery-image' && link);
369
+ case ':local-link':
370
+ return (type === 'link' || type === 'file' || type === 'gallery-image')
371
+ && link instanceof Title && link.title === '';
372
+ case ':read-only':
373
+ return fixed;
374
+ case ':read-write':
375
+ return !fixed;
376
+ case ':invalid':
377
+ return type === 'table-inter' || tokenName === 'HiddenToken';
378
+ case ':required':
379
+ return this.#isProtected() === true;
380
+ case ':optional':
381
+ return this.#isProtected() === false;
382
+ default: {
383
+ const [t, n] = selector.split('#');
384
+ return (!t || t === type || Boolean(Parser.typeAliases[type]?.includes(t)))
385
+ && (!n || n === name);
386
+ }
387
+ }
388
+ }
389
+ else if (selector.length === 4) { // 情形2:属性选择器
390
+ return this.matchesAttr(...selector);
391
+ }
392
+ const [s, pseudo] = selector; // 情形3:复杂伪选择器
393
+ switch (pseudo) {
394
+ case 'is':
395
+ return this.matches(s);
396
+ case 'not':
397
+ return !this.matches(s);
398
+ case 'nth-child':
399
+ return nth(s, index);
400
+ case 'nth-of-type':
401
+ return nth(s, indexOfType);
402
+ case 'nth-last-child':
403
+ return nth(s, lastIndex);
404
+ case 'nth-last-of-type':
405
+ return nth(s, lastIndexOfType);
406
+ case 'contains':
407
+ return this.text().includes(s);
408
+ case 'has':
409
+ return Boolean(this.querySelector(s));
410
+ case 'lang': {
411
+ const regex = new RegExp(`^${s}(?:-|$)`, 'u');
412
+ return matchesLang(this, regex)
413
+ || this.getAncestors().some(ancestor => matchesLang(ancestor, regex));
414
+ }
415
+ case 'regex': {
416
+ const mt = /^([^,]+),\s*\/(.+)\/([a-z]*)$/u.exec(s);
417
+ if (!mt) {
418
+ throw new SyntaxError('错误的伪选择器用法。请使用形如 ":regex(attr, /re/i)" 的格式。');
419
+ }
420
+ try {
421
+ const regex = new RegExp(mt[2], mt[3]);
422
+ return regex.test(String(this.getAttribute(mt[1].trim())));
423
+ }
424
+ catch {
425
+ throw new SyntaxError(`错误的正则表达式:/${mt[2]}/${mt[3]}`);
426
+ }
427
+ }
428
+ default:
429
+ throw new SyntaxError(`未定义的伪选择器:${pseudo}`);
430
+ }
431
+ });
432
+ }
433
+ /**
434
+ * 检查是否符合解析后的选择器
435
+ * @param condition 解析后的选择器
436
+ */
437
+ #matchesArray(condition) {
438
+ const step = condition.pop();
439
+ if (this.#matches(step)) {
440
+ const { parentNode, previousElementSibling } = this;
441
+ switch (condition.at(-1)?.relation) {
442
+ case undefined:
443
+ return true;
444
+ case '>':
445
+ return Boolean(parentNode && parentNode.#matchesArray(condition));
446
+ case '+':
447
+ return Boolean(previousElementSibling && previousElementSibling.#matchesArray(condition));
448
+ case '~': {
449
+ if (!parentNode) {
450
+ return false;
451
+ }
452
+ const { children } = parentNode, i = children.indexOf(this);
453
+ return children.slice(0, i).some(child => child.#matchesArray(condition));
454
+ }
455
+ default: // ' '
456
+ return this.getAncestors().some(ancestor => ancestor.#matchesArray(condition));
457
+ }
458
+ }
459
+ return false;
460
+ }
461
+ /**
462
+ * 检查是否符合解析后的组合选择器
463
+ * @param stack 解析后的一组选择器
464
+ */
465
+ #matchesStack(stack) {
466
+ return stack.some(condition => this.#matchesArray([...condition]));
467
+ }
468
+ /** 检查是否符合选择器 */
469
+ matches(selector) {
470
+ if (selector === undefined) {
471
+ return true;
472
+ }
473
+ return typeof selector === 'string'
474
+ ? this.#matchesStack(parseSelector(selector))
475
+ : this.typeError('matches', 'String');
476
+ }
477
+ /**
478
+ * 符合组合选择器的第一个后代节点
479
+ * @param stack 解析后的一组选择器
480
+ */
481
+ #queryStack(stack) {
482
+ for (const child of this.children) {
483
+ if (child.#matchesStack(stack)) {
484
+ return child;
485
+ }
486
+ const descendant = child.#queryStack(stack);
487
+ if (descendant) {
488
+ return descendant;
489
+ }
490
+ }
491
+ return undefined;
492
+ }
493
+ /** 符合选择器的第一个后代节点 */
494
+ querySelector(selector) {
495
+ return this.#queryStack(parseSelector(selector));
496
+ }
497
+ /**
498
+ * 符合组合选择器的所有后代节点
499
+ * @param stack 解析后的一组选择器
500
+ */
501
+ #queryStackAll(stack) {
502
+ const descendants = [];
503
+ for (const child of this.children) {
504
+ if (child.#matchesStack(stack)) {
505
+ descendants.push(child);
506
+ }
507
+ descendants.push(...child.#queryStackAll(stack));
508
+ }
509
+ return descendants;
510
+ }
511
+ /** 符合选择器的所有后代节点 */
512
+ querySelectorAll(selector) {
513
+ return this.#queryStackAll(parseSelector(selector));
514
+ }
515
+ /**
516
+ * id选择器
517
+ * @param id id名
518
+ */
519
+ getElementById(id) {
520
+ if (typeof id === 'string') {
521
+ const eid = id.replace(/(?<!\\)"/gu, '\\"');
522
+ return this.querySelector(`ext[id="${eid}"], html[id="${eid}"]`);
523
+ }
524
+ return this.typeError('getElementById', 'String');
525
+ }
526
+ /**
527
+ * 类选择器
528
+ * @param className 类名之一
529
+ */
530
+ getElementsByClassName(className) {
531
+ return typeof className === 'string'
532
+ ? this.querySelectorAll(`[className~="${className.replace(/(?<!\\)"/gu, '\\"')}"]`)
533
+ : this.typeError('getElementsByClassName', 'String');
534
+ }
535
+ /**
536
+ * 标签名选择器
537
+ * @param name 标签名
538
+ */
539
+ getElementsByTagName(name) {
540
+ if (typeof name === 'string') {
541
+ const ename = name.replace(/(?<!\\)"/gu, '\\"');
542
+ return this.querySelectorAll(`ext[name="${ename}"], html[name="${ename}"]`);
543
+ }
544
+ return this.typeError('getElementsByTagName', 'String');
545
+ }
546
+ /**
547
+ * 获取某一行的wikitext
548
+ * @param n 行号
549
+ */
550
+ getLine(n) {
551
+ return String(this).split('\n', n + 1)[n];
552
+ }
553
+ /**
554
+ * 在开头批量插入子节点
555
+ * @param elements 插入节点
556
+ */
557
+ prepend(...elements) {
558
+ for (let i = 0; i < elements.length; i++) {
559
+ this.insertAt(elements[i], i);
560
+ }
561
+ }
562
+ /**
563
+ * 获取子节点的位置
564
+ * @param node 子节点
565
+ * @throws `RangeError` 找不到子节点
566
+ */
567
+ #getChildIndex(node) {
568
+ const i = this.childNodes.indexOf(node);
569
+ if (i === -1) {
570
+ Parser.error('找不到子节点!', node);
571
+ throw new RangeError('找不到子节点!');
572
+ }
573
+ return i;
574
+ }
575
+ /**
576
+ * 移除子节点
577
+ * @param node 子节点
578
+ */
579
+ removeChild(node) {
580
+ this.removeAt(this.#getChildIndex(node));
581
+ return node;
582
+ }
583
+ /** @ignore */
584
+ appendChild(node) {
585
+ return this.insertAt(node);
586
+ }
587
+ /** @ignore */
588
+ insertBefore(child, reference) {
589
+ return reference === undefined
590
+ ? this.insertAt(child)
591
+ : this.insertAt(child, this.#getChildIndex(reference));
592
+ }
593
+ /**
594
+ * 替换子节点
595
+ * @param newChild 新子节点
596
+ * @param oldChild 原子节点
597
+ */
598
+ replaceChild(newChild, oldChild) {
599
+ const i = this.#getChildIndex(oldChild);
600
+ this.removeAt(i);
601
+ this.insertAt(newChild, i);
602
+ return oldChild;
603
+ }
604
+ /**
605
+ * 输出AST
606
+ * @param depth 当前深度
607
+ */
608
+ echo(depth = 0) {
609
+ if (!Number.isInteger(depth) || depth < 0) {
610
+ this.typeError('print', 'Number');
611
+ }
612
+ const indent = ' '.repeat(depth), str = String(this), { childNodes, type, length } = this;
613
+ if (childNodes.every(child => child.type === 'text' || !String(child))) {
614
+ console.log(`${indent}\x1B[32m<%s>\x1B[0m${(0, string_1.noWrap)(str)}\x1B[32m</%s>\x1B[0m`, type, type);
615
+ return;
616
+ }
617
+ Parser.info(`${indent}<${type}>`);
618
+ let i = this.getPadding();
619
+ if (i) {
620
+ console.log(`${indent} ${(0, string_1.noWrap)(str.slice(0, i))}`);
621
+ }
622
+ for (let j = 0; j < length; j++) {
623
+ const child = childNodes[j], childStr = String(child), gap = j === length - 1 ? 0 : this.getGaps(j);
624
+ if (!childStr) {
625
+ // pass
626
+ }
627
+ else if (child.type === 'text') {
628
+ console.log(`${indent} ${(0, string_1.noWrap)(String(child))}`);
629
+ }
630
+ else {
631
+ child.echo(depth + 1);
632
+ }
633
+ i += childStr.length;
634
+ if (gap) {
635
+ console.log(`${indent} ${(0, string_1.noWrap)(str.slice(i, i + gap))}`);
636
+ i += gap;
637
+ }
638
+ }
639
+ if (i < str.length) {
640
+ console.log(`${indent} ${(0, string_1.noWrap)(str.slice(i))}`);
641
+ }
642
+ Parser.info(`${indent}</${type}>`);
643
+ }
644
+ }
645
+ /**
646
+ * 检测:lang()伪选择器
647
+ * @param node 节点
648
+ * @param node.attributes 节点属性
649
+ * @param regex 语言正则
650
+ */
651
+ const matchesLang = ({ attributes }, regex) => {
652
+ const lang = attributes?.['lang'];
653
+ return typeof lang === 'string' && regex.test(lang);
654
+ };
655
+ Parser.classes['AstElement'] = __filename;
656
+ module.exports = AstElement;