wikiparser-node 1.13.1-b → 1.13.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.
- package/config/.schema.json +203 -0
- package/config/default.json +32 -31
- package/config/enwiki.json +829 -15
- package/config/llwiki.json +56 -21
- package/config/moegirl.json +65 -21
- package/config/zhwiki.json +498 -32
- package/dist/addon/table.js +494 -0
- package/dist/addon/token.js +392 -0
- package/dist/addon/transclude.js +184 -0
- package/dist/base.d.ts +110 -0
- package/dist/base.js +67 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +279 -0
- package/dist/internal.d.ts +47 -0
- package/dist/lib/element.d.ts +125 -0
- package/dist/lib/element.js +364 -0
- package/dist/lib/node.d.ts +173 -0
- package/dist/lib/node.js +478 -0
- package/dist/lib/range.d.ts +105 -0
- package/dist/lib/range.js +406 -0
- package/dist/lib/ranges.d.ts +28 -0
- package/dist/lib/ranges.js +126 -0
- package/dist/lib/rect.d.ts +18 -0
- package/dist/lib/rect.js +36 -0
- package/dist/lib/text.d.ts +58 -0
- package/dist/lib/text.js +414 -0
- package/dist/lib/title.d.ts +49 -0
- package/dist/lib/title.js +251 -0
- package/dist/mixin/attributesParent.d.ts +49 -0
- package/dist/mixin/attributesParent.js +80 -0
- package/dist/mixin/fixed.d.ts +5 -0
- package/dist/mixin/fixed.js +32 -0
- package/dist/mixin/flagsParent.d.ts +43 -0
- package/dist/mixin/flagsParent.js +64 -0
- package/dist/mixin/hidden.d.ts +7 -0
- package/dist/mixin/hidden.js +39 -0
- package/dist/mixin/magicLinkParent.d.ts +19 -0
- package/dist/mixin/magicLinkParent.js +43 -0
- package/dist/mixin/singleLine.d.ts +5 -0
- package/dist/mixin/singleLine.js +25 -0
- package/dist/mixin/sol.d.ts +6 -0
- package/dist/mixin/sol.js +45 -0
- package/dist/mixin/syntax.d.ts +8 -0
- package/dist/mixin/syntax.js +46 -0
- package/dist/parser/braces.js +152 -0
- package/dist/parser/commentAndExt.js +84 -0
- package/dist/parser/converter.js +41 -0
- package/dist/parser/externalLinks.js +39 -0
- package/dist/parser/hrAndDoubleUnderscore.js +44 -0
- package/dist/parser/html.js +40 -0
- package/dist/parser/links.js +103 -0
- package/dist/parser/list.js +116 -0
- package/dist/parser/magicLinks.js +55 -0
- package/dist/parser/quotes.js +69 -0
- package/dist/parser/redirect.js +28 -0
- package/dist/parser/selector.js +443 -0
- package/dist/parser/table.js +125 -0
- package/dist/src/arg.d.ts +49 -0
- package/dist/src/arg.js +220 -0
- package/dist/src/atom.d.ts +14 -0
- package/dist/src/atom.js +54 -0
- package/dist/src/attribute.d.ts +64 -0
- package/dist/src/attribute.js +482 -0
- package/dist/src/attributes.d.ts +103 -0
- package/dist/src/attributes.js +383 -0
- package/dist/src/converter.d.ts +28 -0
- package/dist/src/converter.js +153 -0
- package/dist/src/converterFlags.d.ts +80 -0
- package/dist/src/converterFlags.js +241 -0
- package/dist/src/converterRule.d.ts +71 -0
- package/dist/src/converterRule.js +218 -0
- package/dist/src/extLink.d.ts +36 -0
- package/dist/src/extLink.js +223 -0
- package/dist/src/gallery.d.ts +51 -0
- package/dist/src/gallery.js +166 -0
- package/dist/src/heading.d.ts +44 -0
- package/dist/src/heading.js +222 -0
- package/dist/src/hidden.d.ts +9 -0
- package/dist/src/hidden.js +82 -0
- package/dist/src/html.d.ts +61 -0
- package/dist/src/html.js +344 -0
- package/dist/src/imageParameter.d.ts +60 -0
- package/dist/src/imageParameter.js +262 -0
- package/dist/src/imagemap.d.ts +47 -0
- package/dist/src/imagemap.js +148 -0
- package/dist/src/imagemapLink.d.ts +35 -0
- package/dist/src/imagemapLink.js +99 -0
- package/dist/src/index.d.ts +136 -0
- package/dist/src/index.js +790 -0
- package/dist/src/link/base.d.ts +52 -0
- package/dist/src/link/base.js +258 -0
- package/dist/src/link/category.d.ts +22 -0
- package/dist/src/link/category.js +36 -0
- package/dist/src/link/file.d.ts +102 -0
- package/dist/src/link/file.js +345 -0
- package/dist/src/link/galleryImage.d.ts +29 -0
- package/dist/src/link/galleryImage.js +133 -0
- package/dist/src/link/index.d.ts +39 -0
- package/dist/src/link/index.js +100 -0
- package/dist/src/link/redirectTarget.d.ts +27 -0
- package/dist/src/link/redirectTarget.js +71 -0
- package/dist/src/magicLink.d.ts +57 -0
- package/dist/src/magicLink.js +261 -0
- package/dist/src/nested.d.ts +40 -0
- package/dist/src/nested.js +108 -0
- package/dist/src/nowiki/base.d.ts +28 -0
- package/dist/src/nowiki/base.js +90 -0
- package/dist/src/nowiki/comment.d.ts +14 -0
- package/dist/src/nowiki/comment.js +123 -0
- package/dist/src/nowiki/dd.d.ts +8 -0
- package/dist/src/nowiki/dd.js +74 -0
- package/dist/src/nowiki/doubleUnderscore.d.ts +15 -0
- package/dist/src/nowiki/doubleUnderscore.js +101 -0
- package/dist/src/nowiki/hr.d.ts +5 -0
- package/dist/src/nowiki/hr.js +72 -0
- package/dist/src/nowiki/index.d.ts +14 -0
- package/dist/src/nowiki/index.js +30 -0
- package/dist/src/nowiki/list.d.ts +5 -0
- package/dist/src/nowiki/list.js +67 -0
- package/dist/src/nowiki/listBase.d.ts +23 -0
- package/dist/src/nowiki/listBase.js +100 -0
- package/dist/src/nowiki/noinclude.d.ts +6 -0
- package/dist/src/nowiki/noinclude.js +77 -0
- package/dist/src/nowiki/quote.d.ts +14 -0
- package/dist/src/nowiki/quote.js +149 -0
- package/dist/src/onlyinclude.d.ts +13 -0
- package/dist/src/onlyinclude.js +60 -0
- package/dist/src/paramTag/index.d.ts +28 -0
- package/dist/src/paramTag/index.js +80 -0
- package/dist/src/paramTag/inputbox.d.ts +8 -0
- package/dist/src/paramTag/inputbox.js +38 -0
- package/dist/src/parameter.d.ts +60 -0
- package/dist/src/parameter.js +267 -0
- package/dist/src/pre.d.ts +28 -0
- package/dist/src/pre.js +70 -0
- package/dist/src/redirect.d.ts +30 -0
- package/dist/src/redirect.js +128 -0
- package/dist/src/syntax.d.ts +15 -0
- package/dist/src/syntax.js +87 -0
- package/dist/src/table/base.d.ts +28 -0
- package/dist/src/table/base.js +81 -0
- package/dist/src/table/index.d.ts +230 -0
- package/dist/src/table/index.js +506 -0
- package/dist/src/table/td.d.ts +72 -0
- package/dist/src/table/td.js +375 -0
- package/dist/src/table/tr.d.ts +30 -0
- package/dist/src/table/tr.js +61 -0
- package/dist/src/table/trBase.d.ts +49 -0
- package/dist/src/table/trBase.js +165 -0
- package/dist/src/tagPair/ext.d.ts +29 -0
- package/dist/src/tagPair/ext.js +229 -0
- package/dist/src/tagPair/include.d.ts +33 -0
- package/dist/src/tagPair/include.js +145 -0
- package/dist/src/tagPair/index.d.ts +23 -0
- package/dist/src/tagPair/index.js +130 -0
- package/dist/src/transclude.d.ts +159 -0
- package/dist/src/transclude.js +598 -0
- package/dist/util/constants.js +26 -0
- package/dist/util/debug.js +95 -0
- package/dist/util/diff.js +83 -0
- package/dist/util/html.js +146 -0
- package/dist/util/lint.js +32 -0
- package/dist/util/string.js +107 -0
- package/errors/README +3 -0
- package/package.json +21 -28
- package/printed/README +3 -0
- package/bundle/bundle.min.js +0 -37
- package/extensions/dist/base.js +0 -163
- package/extensions/dist/codejar.js +0 -53
- package/extensions/dist/editor.js +0 -159
- package/extensions/dist/highlight.js +0 -30
- package/extensions/dist/lint.js +0 -72
- package/extensions/editor.css +0 -59
- package/extensions/ui.css +0 -162
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCondition = void 0;
|
|
4
|
+
const constants_1 = require("../util/constants");
|
|
5
|
+
const ranges_1 = require("../lib/ranges");
|
|
6
|
+
const title_1 = require("../lib/title");
|
|
7
|
+
/* NOT FOR BROWSER */
|
|
8
|
+
const simplePseudos = new Set([
|
|
9
|
+
'root',
|
|
10
|
+
'first-child',
|
|
11
|
+
'first-of-type',
|
|
12
|
+
'last-child',
|
|
13
|
+
'last-of-type',
|
|
14
|
+
'only-child',
|
|
15
|
+
'only-of-type',
|
|
16
|
+
'empty',
|
|
17
|
+
'parent',
|
|
18
|
+
'header',
|
|
19
|
+
'hidden',
|
|
20
|
+
'visible',
|
|
21
|
+
'only-whitespace',
|
|
22
|
+
'any-link',
|
|
23
|
+
'local-link',
|
|
24
|
+
'invalid',
|
|
25
|
+
'valid',
|
|
26
|
+
'required',
|
|
27
|
+
'optional',
|
|
28
|
+
'scope',
|
|
29
|
+
]), complexPseudos = new Set([
|
|
30
|
+
'is',
|
|
31
|
+
'not',
|
|
32
|
+
'nth-child',
|
|
33
|
+
'nth-of-type',
|
|
34
|
+
'nth-last-child',
|
|
35
|
+
'nth-last-of-type',
|
|
36
|
+
'contains',
|
|
37
|
+
'has',
|
|
38
|
+
'lang',
|
|
39
|
+
'regex',
|
|
40
|
+
]), specialChars = [
|
|
41
|
+
['[', '['],
|
|
42
|
+
[']', ']'],
|
|
43
|
+
['(', '('],
|
|
44
|
+
[')', ')'],
|
|
45
|
+
['"', '"'],
|
|
46
|
+
[`'`, '''],
|
|
47
|
+
[':', ':'],
|
|
48
|
+
['\\', '\'],
|
|
49
|
+
['&', '&'],
|
|
50
|
+
], regularRegex = /[[(,>+~]|\s+/u, attributeRegex = /^\s*(\w+)\s*(?:([~|^$*!]?=)\s*("[^"]*"|'[^']*'|[^\s[\]]+)(?:\s+(i))?\s*)?\]/u, functionRegex = /^(\s*"[^"]*"\s*|\s*'[^']*'\s*|[^()]*)\)/u, grouping = new Set([',', '>', '+', '~']), combinator = new Set(['>', '+', '~', '']), primitives = new Set(['string', 'number', 'boolean', 'undefined']);
|
|
51
|
+
/**
|
|
52
|
+
* optionally convert to lower cases
|
|
53
|
+
* @param val 属性值
|
|
54
|
+
* @param i 是否对大小写不敏感
|
|
55
|
+
*/
|
|
56
|
+
const toCase = (val, i) => i ? val.toLowerCase() : val;
|
|
57
|
+
/**
|
|
58
|
+
* 检查某个下标是否符合表达式
|
|
59
|
+
* @param str 表达式
|
|
60
|
+
* @param i 待检查的下标
|
|
61
|
+
*/
|
|
62
|
+
const nth = (str, i) => new ranges_1.Ranges(str).applyTo(i + 1).includes(i);
|
|
63
|
+
/**
|
|
64
|
+
* 是否受保护。保护条件来自Token,这里仅提前用于:required和:optional伪选择器。
|
|
65
|
+
* @param token 节点
|
|
66
|
+
*/
|
|
67
|
+
const isProtected = (token) => {
|
|
68
|
+
const { parentNode } = token;
|
|
69
|
+
if (!parentNode) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const { childNodes, fixed } = parentNode;
|
|
73
|
+
return fixed
|
|
74
|
+
|| parentNode.getAttribute('protectedChildren').applyTo(childNodes).includes(childNodes.indexOf(token));
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* 获取属性
|
|
78
|
+
* @param token 节点
|
|
79
|
+
* @param key 属性键
|
|
80
|
+
*/
|
|
81
|
+
const getAttr = (token, key) => {
|
|
82
|
+
if (typeof token.getAttr === 'function') {
|
|
83
|
+
const attr = token.getAttr(key);
|
|
84
|
+
if (attr !== undefined) {
|
|
85
|
+
return attr;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const val = token[key];
|
|
89
|
+
return val instanceof RegExp ? val.source : val;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* 检查是否符合解析后的选择器,不含节点关系
|
|
93
|
+
* @param token 节点
|
|
94
|
+
* @param step 解析后的选择器
|
|
95
|
+
* @param scope 作用对象
|
|
96
|
+
* @param has `:has()`伪选择器
|
|
97
|
+
* @throws `SyntaxError` 错误的正则伪选择器
|
|
98
|
+
* @throws `SyntaxError` 未定义的伪选择器
|
|
99
|
+
*/
|
|
100
|
+
const matches = (token, step, scope, has) => {
|
|
101
|
+
const { parentNode, type, name, childNodes, link } = token, invalid = type === 'table-inter' || type === 'image-parameter' && name === 'invalid', children = parentNode?.children, childrenOfType = children?.filter(({ type: t }) => t === type), siblingsCount = children?.length ?? 1, siblingsCountOfType = childrenOfType?.length ?? 1, index = (children?.indexOf(token) ?? 0) + 1, indexOfType = (childrenOfType?.indexOf(token) ?? 0) + 1, lastIndex = siblingsCount - index + 1, lastIndexOfType = siblingsCountOfType - indexOfType + 1;
|
|
102
|
+
return step.every(selector => {
|
|
103
|
+
if (typeof selector === 'string') {
|
|
104
|
+
switch (selector) { // 情形1:简单伪选择器、type和name
|
|
105
|
+
case '':
|
|
106
|
+
return token === has;
|
|
107
|
+
case '*':
|
|
108
|
+
return true;
|
|
109
|
+
case ':root':
|
|
110
|
+
return !parentNode;
|
|
111
|
+
case ':first-child':
|
|
112
|
+
return index === 1;
|
|
113
|
+
case ':first-of-type':
|
|
114
|
+
return indexOfType === 1;
|
|
115
|
+
case ':last-child':
|
|
116
|
+
return lastIndex === 1;
|
|
117
|
+
case ':last-of-type':
|
|
118
|
+
return lastIndexOfType === 1;
|
|
119
|
+
case ':only-child':
|
|
120
|
+
return siblingsCount === 1;
|
|
121
|
+
case ':only-of-type':
|
|
122
|
+
return siblingsCountOfType === 1;
|
|
123
|
+
case ':empty':
|
|
124
|
+
return !childNodes.some(({ type: t, data }) => t !== 'text' || data);
|
|
125
|
+
case ':parent':
|
|
126
|
+
return childNodes.some(({ type: t, data }) => t !== 'text' || data);
|
|
127
|
+
case ':header':
|
|
128
|
+
return type === 'heading';
|
|
129
|
+
case ':hidden':
|
|
130
|
+
return token.text() === '';
|
|
131
|
+
case ':visible':
|
|
132
|
+
return token.text() !== '';
|
|
133
|
+
case ':only-whitespace':
|
|
134
|
+
return token.text().trim() === '';
|
|
135
|
+
case ':any-link':
|
|
136
|
+
return type === 'link'
|
|
137
|
+
|| type === 'redirect-target'
|
|
138
|
+
|| type === 'free-ext-link'
|
|
139
|
+
|| type === 'magic-link'
|
|
140
|
+
|| type === 'ext-link'
|
|
141
|
+
|| (type === 'file' || type === 'gallery-image') && link;
|
|
142
|
+
case ':local-link':
|
|
143
|
+
return (type === 'link' || type === 'file' || type === 'gallery-image')
|
|
144
|
+
&& link instanceof title_1.Title
|
|
145
|
+
&& link.title === '';
|
|
146
|
+
case ':invalid':
|
|
147
|
+
return invalid;
|
|
148
|
+
case ':valid':
|
|
149
|
+
return !invalid;
|
|
150
|
+
case ':required':
|
|
151
|
+
return isProtected(token) === true;
|
|
152
|
+
case ':optional':
|
|
153
|
+
return isProtected(token) === false;
|
|
154
|
+
case ':scope':
|
|
155
|
+
return token === scope;
|
|
156
|
+
default: {
|
|
157
|
+
const [t, n] = selector.split('#');
|
|
158
|
+
return (!t || t === type) && (!n || n === name);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else if (selector.length === 4) { // 情形2:属性选择器
|
|
163
|
+
const [key, equal, val = '', i] = selector, isAttr = typeof token.hasAttr === 'function' && typeof token.getAttr === 'function';
|
|
164
|
+
if (!(key in token) && (!isAttr || !token.hasAttr(key))) {
|
|
165
|
+
return equal === '!=';
|
|
166
|
+
}
|
|
167
|
+
const v = toCase(val, i), thisVal = getAttr(token, key);
|
|
168
|
+
if (!equal) {
|
|
169
|
+
return thisVal !== undefined && thisVal !== false;
|
|
170
|
+
}
|
|
171
|
+
if (equal === '~=') {
|
|
172
|
+
const thisVals = typeof thisVal === 'string' ? thisVal.split(/\s/u) : thisVal;
|
|
173
|
+
return Boolean(thisVals?.[Symbol.iterator])
|
|
174
|
+
&& [...thisVals].some(w => typeof w === 'string' && toCase(w, i) === v);
|
|
175
|
+
}
|
|
176
|
+
else if (!primitives.has(typeof thisVal) && !(thisVal instanceof title_1.Title)) {
|
|
177
|
+
throw new RangeError(`The complex attribute ${key} cannot be used in a selector!`);
|
|
178
|
+
}
|
|
179
|
+
const stringVal = toCase(String(thisVal), i);
|
|
180
|
+
switch (equal) {
|
|
181
|
+
case '|=':
|
|
182
|
+
return stringVal === v || stringVal.startsWith(`${v}-`);
|
|
183
|
+
case '^=':
|
|
184
|
+
return stringVal.startsWith(v);
|
|
185
|
+
case '$=':
|
|
186
|
+
return stringVal.endsWith(v);
|
|
187
|
+
case '*=':
|
|
188
|
+
return stringVal.includes(v);
|
|
189
|
+
case '!=':
|
|
190
|
+
return stringVal !== v;
|
|
191
|
+
default: // `=`
|
|
192
|
+
return stringVal === v;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const [s, pseudo] = selector; // 情形3:复杂伪选择器
|
|
196
|
+
switch (pseudo) {
|
|
197
|
+
case 'is':
|
|
198
|
+
return (0, exports.getCondition)(s, scope)(token);
|
|
199
|
+
case 'not':
|
|
200
|
+
return !(0, exports.getCondition)(s, scope)(token);
|
|
201
|
+
case 'nth-child':
|
|
202
|
+
return nth(s, index);
|
|
203
|
+
case 'nth-of-type':
|
|
204
|
+
return nth(s, indexOfType);
|
|
205
|
+
case 'nth-last-child':
|
|
206
|
+
return nth(s, lastIndex);
|
|
207
|
+
case 'nth-last-of-type':
|
|
208
|
+
return nth(s, lastIndexOfType);
|
|
209
|
+
case 'contains':
|
|
210
|
+
return token.text().includes(s);
|
|
211
|
+
case 'has': {
|
|
212
|
+
if (has) {
|
|
213
|
+
throw new SyntaxError('The :has() pseudo-selector cannot be nested.');
|
|
214
|
+
}
|
|
215
|
+
const condition = (0, exports.getCondition)(s, scope, token), childOrSibling = children && /(?:^|,)\s*[+~]/u.test(s)
|
|
216
|
+
? [...token.childNodes, ...children.slice(children.indexOf(token))]
|
|
217
|
+
: token.childNodes;
|
|
218
|
+
/**
|
|
219
|
+
* 递归查找元素
|
|
220
|
+
* @param child 子节点
|
|
221
|
+
*/
|
|
222
|
+
const hasElement = (child) => child.type !== 'text' && (condition(child) || child.childNodes.some(hasElement));
|
|
223
|
+
return childOrSibling.some(hasElement);
|
|
224
|
+
}
|
|
225
|
+
case 'lang': {
|
|
226
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
227
|
+
/^zh(?:-|$)/iu;
|
|
228
|
+
const regex = new RegExp(`^${s}(?:-|$)`, 'iu');
|
|
229
|
+
let node = token;
|
|
230
|
+
for (; node; node = node.parentNode) {
|
|
231
|
+
const lang = node.attributes?.['lang'];
|
|
232
|
+
if (lang !== undefined) {
|
|
233
|
+
return typeof lang === 'string' && regex.test(lang);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
case 'regex': {
|
|
239
|
+
const mt = /^([^,]+),\s*\/(.+)\/([a-z]*)$/u.exec(s);
|
|
240
|
+
if (!mt) {
|
|
241
|
+
throw new SyntaxError(`Wrong usage of the regex pseudo-selector. Use ":regex('attr, /re/i')" format.`);
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
return new RegExp(mt[2], mt[3]).test(String(getAttr(token, mt[1].trim())));
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
throw new SyntaxError(`Invalid regular expression: /${mt[2]}/${mt[3]}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
default:
|
|
251
|
+
throw new SyntaxError(`Undefined pseudo-selector: ${pseudo}`);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
};
|
|
255
|
+
/**
|
|
256
|
+
* 检查是否符合解析后的选择器
|
|
257
|
+
* @param token 节点
|
|
258
|
+
* @param copy 解析后的选择器
|
|
259
|
+
* @param scope 作用对象
|
|
260
|
+
* @param has `:has()`伪选择器
|
|
261
|
+
*/
|
|
262
|
+
const matchesArray = (token, copy, scope, has) => {
|
|
263
|
+
const condition = [...copy];
|
|
264
|
+
if (matches(token, condition.pop(), scope, has)) {
|
|
265
|
+
const { parentNode, previousElementSibling } = token;
|
|
266
|
+
switch (condition.at(-1)?.relation) {
|
|
267
|
+
case undefined:
|
|
268
|
+
return true;
|
|
269
|
+
case '>':
|
|
270
|
+
return Boolean(parentNode && matchesArray(parentNode, condition, scope, has));
|
|
271
|
+
case '+':
|
|
272
|
+
return Boolean(previousElementSibling && matchesArray(previousElementSibling, condition, scope, has));
|
|
273
|
+
case '~': {
|
|
274
|
+
if (!parentNode) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
const { children } = parentNode;
|
|
278
|
+
return children.slice(0, children.indexOf(token))
|
|
279
|
+
.some(child => matchesArray(child, condition, scope, has));
|
|
280
|
+
}
|
|
281
|
+
default: // ' '
|
|
282
|
+
return token.getAncestors().some(ancestor => matchesArray(ancestor, condition, scope, has));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return false;
|
|
286
|
+
};
|
|
287
|
+
/**
|
|
288
|
+
* 还原转义符号
|
|
289
|
+
* @param selector
|
|
290
|
+
*/
|
|
291
|
+
const desanitize = (selector) => {
|
|
292
|
+
for (const [c, entity] of specialChars) {
|
|
293
|
+
selector = selector.replaceAll(entity, c);
|
|
294
|
+
}
|
|
295
|
+
return selector.trim();
|
|
296
|
+
};
|
|
297
|
+
/**
|
|
298
|
+
* 去除首尾的引号
|
|
299
|
+
* @param val 属性值或伪选择器函数的参数
|
|
300
|
+
*/
|
|
301
|
+
const deQuote = (val) => /^(["']).*\1$/u.test(val) ? val.slice(1, -1) : val.trim();
|
|
302
|
+
/**
|
|
303
|
+
* 检查节点是否符合选择器
|
|
304
|
+
* @param selector
|
|
305
|
+
* @param scope 作用对象
|
|
306
|
+
* @param has `:has()`伪选择器
|
|
307
|
+
*/
|
|
308
|
+
const checkToken = (selector, scope, has) => (token) => {
|
|
309
|
+
let sanitized = selector.trim();
|
|
310
|
+
for (const [c, entity] of specialChars) {
|
|
311
|
+
sanitized = sanitized.replaceAll(`\\${c}`, entity);
|
|
312
|
+
}
|
|
313
|
+
const stack = [[[]]];
|
|
314
|
+
let regex = regularRegex, mt = regex.exec(sanitized), [condition] = stack, [step] = condition;
|
|
315
|
+
/**
|
|
316
|
+
* 解析简单伪选择器
|
|
317
|
+
* @param index 伪选择器的终点位置
|
|
318
|
+
* @throws `SyntaxError` 选择器排序
|
|
319
|
+
* @throws `SyntaxError` 非法的选择器
|
|
320
|
+
*/
|
|
321
|
+
const pushSimple = (index) => {
|
|
322
|
+
const str = sanitized.slice(0, index).trim();
|
|
323
|
+
if (!str) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const pieces = str.split(/(?=[:#])/u);
|
|
327
|
+
for (let i = 0; i < pieces.length; i++) {
|
|
328
|
+
const piece = pieces[i];
|
|
329
|
+
if (!/^[:#]/u.test(piece)) {
|
|
330
|
+
if (step.length > 0) {
|
|
331
|
+
throw new SyntaxError(`Invalid selector!\n${selector}\nType selectors must come first.`);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
step.push(piece);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
else if (piece.startsWith(':')) {
|
|
338
|
+
if (simplePseudos.has(piece.slice(1))) {
|
|
339
|
+
step.push(piece);
|
|
340
|
+
}
|
|
341
|
+
else if (pieces[i - 1]?.startsWith('#')) {
|
|
342
|
+
pieces[i - 1] += piece;
|
|
343
|
+
pieces.splice(i, 1);
|
|
344
|
+
i--;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
throw new SyntaxError(`Undefined pseudo selector!\n${desanitize(piece)}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
step.push(...pieces.filter(piece => piece.startsWith('#')).map(desanitize));
|
|
352
|
+
};
|
|
353
|
+
/**
|
|
354
|
+
* 检查是否需要通用选择器
|
|
355
|
+
* @throws `SyntaxError` 非法的选择器
|
|
356
|
+
*/
|
|
357
|
+
const needUniversal = () => {
|
|
358
|
+
if (step.length === 0 && (condition.length > 1 || !has)) {
|
|
359
|
+
throw new SyntaxError(`Invalid selector!\n${selector}\nYou may need the universal selector '*'.`);
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
while (mt) {
|
|
363
|
+
let { 0: syntax, index } = mt;
|
|
364
|
+
if (syntax.trim() === '') {
|
|
365
|
+
index += syntax.length;
|
|
366
|
+
const char = sanitized[index];
|
|
367
|
+
syntax = grouping.has(char) ? char : '';
|
|
368
|
+
}
|
|
369
|
+
if (syntax === ',') { // 情形1:并列
|
|
370
|
+
pushSimple(index);
|
|
371
|
+
needUniversal();
|
|
372
|
+
condition = [[]];
|
|
373
|
+
[step] = condition;
|
|
374
|
+
stack.push(condition);
|
|
375
|
+
}
|
|
376
|
+
else if (combinator.has(syntax)) { // 情形2:关系
|
|
377
|
+
if (has && syntax && condition.length === 1 && step.length === 0 && !sanitized.slice(0, index).trim()) {
|
|
378
|
+
step.push('');
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
pushSimple(index);
|
|
382
|
+
}
|
|
383
|
+
needUniversal();
|
|
384
|
+
step.relation = syntax;
|
|
385
|
+
step = [];
|
|
386
|
+
condition.push(step);
|
|
387
|
+
}
|
|
388
|
+
else if (syntax === '[') { // 情形3:属性开启
|
|
389
|
+
pushSimple(index);
|
|
390
|
+
regex = attributeRegex;
|
|
391
|
+
}
|
|
392
|
+
else if (syntax.endsWith(']')) { // 情形4:属性闭合
|
|
393
|
+
mt[3] &&= desanitize(deQuote(mt[3]));
|
|
394
|
+
step.push(mt.slice(1));
|
|
395
|
+
regex = regularRegex;
|
|
396
|
+
}
|
|
397
|
+
else if (syntax === '(') { // 情形5:伪选择器开启
|
|
398
|
+
const i = sanitized.lastIndexOf(':', index), pseudo = sanitized.slice(i + 1, index);
|
|
399
|
+
if (i === -1 || !complexPseudos.has(pseudo)) {
|
|
400
|
+
throw new SyntaxError(`Undefined pseudo selector!\n${desanitize(sanitized)}`);
|
|
401
|
+
}
|
|
402
|
+
pushSimple(i);
|
|
403
|
+
step.push(pseudo); // 临时存放复杂伪选择器
|
|
404
|
+
regex = functionRegex;
|
|
405
|
+
}
|
|
406
|
+
else { // 情形6:伪选择器闭合
|
|
407
|
+
mt.push(step.pop());
|
|
408
|
+
mt[1] &&= deQuote(mt[1]);
|
|
409
|
+
step.push(mt.slice(1));
|
|
410
|
+
regex = regularRegex;
|
|
411
|
+
}
|
|
412
|
+
sanitized = sanitized.slice(index + syntax.length);
|
|
413
|
+
if (grouping.has(syntax)) {
|
|
414
|
+
sanitized = sanitized.trim();
|
|
415
|
+
}
|
|
416
|
+
mt = regex.exec(sanitized);
|
|
417
|
+
}
|
|
418
|
+
if (regex === regularRegex) {
|
|
419
|
+
pushSimple();
|
|
420
|
+
needUniversal();
|
|
421
|
+
return stack.some(copy => matchesArray(token, copy, scope, has));
|
|
422
|
+
}
|
|
423
|
+
throw new SyntaxError(`Unclosed '${regex === attributeRegex ? '[' : '('}' in the selector!\n${desanitize(sanitized)}`);
|
|
424
|
+
};
|
|
425
|
+
/* NOT FOR BROWSER END */
|
|
426
|
+
/**
|
|
427
|
+
* 将选择器转化为类型谓词
|
|
428
|
+
* @param selector 选择器
|
|
429
|
+
* @param scope 作用对象
|
|
430
|
+
* @param has `:has()`伪选择器
|
|
431
|
+
*/
|
|
432
|
+
const getCondition = (selector, scope, has) => (
|
|
433
|
+
/* eslint-disable @stylistic/operator-linebreak */
|
|
434
|
+
/[^a-z\-,#]/u.test(selector) ?
|
|
435
|
+
checkToken(selector, scope, has) :
|
|
436
|
+
({ type, name }) => selector.split(',').some(str => {
|
|
437
|
+
const [t, ...ns] = str.trim().split('#');
|
|
438
|
+
return (!t || t === type) && ns.every(n => n === name);
|
|
439
|
+
})
|
|
440
|
+
/* eslint-enable @stylistic/operator-linebreak */
|
|
441
|
+
);
|
|
442
|
+
exports.getCondition = getCondition;
|
|
443
|
+
constants_1.parsers['parseSelector'] = __filename;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseTable = void 0;
|
|
4
|
+
const index_1 = require("../src/index");
|
|
5
|
+
const index_2 = require("../src/table/index");
|
|
6
|
+
const tr_1 = require("../src/table/tr");
|
|
7
|
+
const td_1 = require("../src/table/td");
|
|
8
|
+
const dd_1 = require("../src/nowiki/dd");
|
|
9
|
+
/* NOT FOR BROWSER */
|
|
10
|
+
const constants_1 = require("../util/constants");
|
|
11
|
+
/* NOT FOR BROWSER END */
|
|
12
|
+
/**
|
|
13
|
+
* 判断是否为表格行或表格
|
|
14
|
+
* @param token 表格节点
|
|
15
|
+
*/
|
|
16
|
+
const isTr = (token) => token.lastChild.constructor !== index_1.Token;
|
|
17
|
+
/**
|
|
18
|
+
* 解析表格,注意`tr`和`td`包含开头的换行
|
|
19
|
+
* @param {Token & {firstChild: AstText}} root 根节点
|
|
20
|
+
* @param config
|
|
21
|
+
* @param accum
|
|
22
|
+
*/
|
|
23
|
+
const parseTable = ({ firstChild: { data }, type, name }, config, accum) => {
|
|
24
|
+
const stack = [], lines = data.split('\n');
|
|
25
|
+
let out = type === 'root' || type === 'parameter-value' || type === 'ext-inner' && name === 'poem'
|
|
26
|
+
? ''
|
|
27
|
+
: `\n${lines.shift()}`, top;
|
|
28
|
+
/**
|
|
29
|
+
* 向表格中插入纯文本
|
|
30
|
+
* @param str 待插入的文本
|
|
31
|
+
* @param topToken 当前解析的表格或表格行
|
|
32
|
+
*/
|
|
33
|
+
const push = (str, topToken) => {
|
|
34
|
+
if (!topToken) {
|
|
35
|
+
out += str;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const { lastChild } = topToken;
|
|
39
|
+
if (isTr(topToken)) {
|
|
40
|
+
const token = new index_1.Token(str, config, accum);
|
|
41
|
+
token.type = 'table-inter';
|
|
42
|
+
token.setAttribute('stage', 3);
|
|
43
|
+
topToken.insertAt(token);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
lastChild.setText(lastChild.toString() + str);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
/** 取出最近的表格行 */
|
|
50
|
+
pop = () => top.type === 'td' ? stack.pop() : top;
|
|
51
|
+
for (const outLine of lines) {
|
|
52
|
+
top = stack.pop();
|
|
53
|
+
const [spaces] = /^(?:\s|\0\d+[cno]\x7F)*/u.exec(outLine), line = outLine.slice(spaces.length), matchesStart = /^(:*)((?:\s|\0\d+[cn]\x7F)*)(\{\||\{(?:\0\d+[cn]\x7F)*\0\d+!\x7F|\0\d+\{\x7F)(.*)$/u
|
|
54
|
+
.exec(line);
|
|
55
|
+
if (matchesStart) {
|
|
56
|
+
while (top && top.type !== 'td') {
|
|
57
|
+
top = stack.pop();
|
|
58
|
+
}
|
|
59
|
+
const [, indent, moreSpaces, tableSyntax, attr] = matchesStart;
|
|
60
|
+
if (indent) {
|
|
61
|
+
// @ts-expect-error abstract class
|
|
62
|
+
new dd_1.DdToken(indent, config, accum);
|
|
63
|
+
}
|
|
64
|
+
push(`\n${spaces}${indent && `\0${accum.length - 1}d\x7F`}${moreSpaces}\0${accum.length}b\x7F`, top);
|
|
65
|
+
// @ts-expect-error abstract class
|
|
66
|
+
stack.push(...top ? [top] : [], new index_2.TableToken(tableSyntax, attr, config, accum));
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
else if (!top) {
|
|
70
|
+
out += `\n${outLine}`;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const matches = // eslint-disable-line @stylistic/operator-linebreak
|
|
74
|
+
/^(?:(\|\}|\0\d+!\x7F\}|\0\d+\}\x7F)|(\|-+|\0\d+!\x7F-+|\0\d+-\x7F-*)(?!-)|(!|(?:\||\0\d+!\x7F)\+?))(.*)$/u
|
|
75
|
+
.exec(line);
|
|
76
|
+
if (!matches) {
|
|
77
|
+
push(`\n${outLine}`, top);
|
|
78
|
+
stack.push(top);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const [, closing, row, cell, attr] = matches;
|
|
82
|
+
if (closing) {
|
|
83
|
+
while (top.type !== 'table') {
|
|
84
|
+
top = stack.pop();
|
|
85
|
+
}
|
|
86
|
+
top.close(`\n${spaces}${closing}`, true);
|
|
87
|
+
push(attr, stack[stack.length - 1]);
|
|
88
|
+
}
|
|
89
|
+
else if (row) {
|
|
90
|
+
top = pop();
|
|
91
|
+
if (top.type === 'tr') {
|
|
92
|
+
top = stack.pop();
|
|
93
|
+
}
|
|
94
|
+
// @ts-expect-error abstract class
|
|
95
|
+
const tr = new tr_1.TrToken(`\n${spaces}${row}`, attr, config, accum);
|
|
96
|
+
stack.push(top, tr);
|
|
97
|
+
top.insertAt(tr);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
top = pop();
|
|
101
|
+
const regex = cell === '!' ? /!!|(?:\||\0\d+!\x7F){2}|\0\d+\+\x7F/gu : /(?:\||\0\d+!\x7F){2}|\0\d+\+\x7F/gu;
|
|
102
|
+
let mt = regex.exec(attr), lastIndex = 0, lastSyntax = `\n${spaces}${cell}`;
|
|
103
|
+
/**
|
|
104
|
+
* 创建表格单元格
|
|
105
|
+
* @param tr 当前表格行
|
|
106
|
+
*/
|
|
107
|
+
const newTd = (tr) => {
|
|
108
|
+
// @ts-expect-error abstract class
|
|
109
|
+
const td = new td_1.TdToken(lastSyntax, attr.slice(lastIndex, mt?.index), config, accum);
|
|
110
|
+
tr.insertAt(td);
|
|
111
|
+
return td;
|
|
112
|
+
};
|
|
113
|
+
while (mt) {
|
|
114
|
+
newTd(top);
|
|
115
|
+
({ lastIndex } = regex);
|
|
116
|
+
[lastSyntax] = mt;
|
|
117
|
+
mt = regex.exec(attr);
|
|
118
|
+
}
|
|
119
|
+
stack.push(top, newTd(top));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return out.slice(1);
|
|
123
|
+
};
|
|
124
|
+
exports.parseTable = parseTable;
|
|
125
|
+
constants_1.parsers['parseTable'] = __filename;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import Parser from '../index';
|
|
2
|
+
import { Token } from './index';
|
|
3
|
+
import { AtomToken } from './atom';
|
|
4
|
+
import { HiddenToken } from './hidden';
|
|
5
|
+
import type { LintError } from '../base';
|
|
6
|
+
/**
|
|
7
|
+
* `{{{}}}`包裹的参数
|
|
8
|
+
* @classdesc `{childNodes: [AtomToken, ?Token, ...HiddenToken]}`
|
|
9
|
+
*/
|
|
10
|
+
export declare abstract class ArgToken extends Token {
|
|
11
|
+
#private;
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly childNodes: readonly [AtomToken] | readonly [AtomToken, Token, ...HiddenToken[]];
|
|
14
|
+
abstract get firstChild(): AtomToken;
|
|
15
|
+
abstract get lastChild(): Token;
|
|
16
|
+
abstract get children(): [AtomToken] | [AtomToken, Token, ...HiddenToken[]];
|
|
17
|
+
abstract get firstElementChild(): AtomToken;
|
|
18
|
+
abstract get lastElementChild(): Token;
|
|
19
|
+
get type(): 'arg';
|
|
20
|
+
/** 预设值 */
|
|
21
|
+
get default(): string | false;
|
|
22
|
+
set default(value: string | false);
|
|
23
|
+
/** @param parts 以'|'分隔的各部分 */
|
|
24
|
+
constructor(parts: readonly string[], config?: Parser.Config, accum?: Token[]);
|
|
25
|
+
cloneNode(): this;
|
|
26
|
+
/** 移除无效部分 */
|
|
27
|
+
removeRedundant(): void;
|
|
28
|
+
/**
|
|
29
|
+
* @override
|
|
30
|
+
* @param i 移除位置
|
|
31
|
+
*/
|
|
32
|
+
removeAt(i: number): Token;
|
|
33
|
+
/**
|
|
34
|
+
* @override
|
|
35
|
+
* @param token 待插入的子节点
|
|
36
|
+
* @param i 插入位置
|
|
37
|
+
*/
|
|
38
|
+
insertAt<T extends Token>(token: T, i?: number): T;
|
|
39
|
+
/**
|
|
40
|
+
* 设置参数名
|
|
41
|
+
* @param name 新参数名
|
|
42
|
+
*/
|
|
43
|
+
setName(name: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* 设置预设值
|
|
46
|
+
* @param value 预设值
|
|
47
|
+
*/
|
|
48
|
+
setDefault(value: string | false): void;
|
|
49
|
+
}
|