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.
- package/.eslintrc.json +472 -34
- package/README.md +1 -1
- package/config/default.json +58 -30
- package/config/llwiki.json +22 -90
- package/config/moegirl.json +51 -13
- package/config/zhwiki.json +1269 -0
- package/index.js +114 -104
- package/lib/element.js +448 -440
- package/lib/node.js +335 -115
- package/lib/ranges.js +27 -18
- package/lib/text.js +146 -0
- package/lib/title.js +13 -5
- package/mixin/attributeParent.js +70 -24
- package/mixin/fixedToken.js +14 -6
- package/mixin/hidden.js +6 -4
- package/mixin/sol.js +27 -10
- package/package.json +9 -3
- package/parser/brackets.js +22 -17
- package/parser/commentAndExt.js +18 -16
- package/parser/converter.js +14 -13
- package/parser/externalLinks.js +12 -11
- package/parser/hrAndDoubleUnderscore.js +23 -14
- package/parser/html.js +10 -9
- package/parser/links.js +15 -14
- package/parser/list.js +12 -11
- package/parser/magicLinks.js +12 -11
- package/parser/quotes.js +6 -5
- package/parser/selector.js +175 -0
- package/parser/table.js +25 -18
- package/printed/example.json +120 -0
- package/src/arg.js +56 -32
- package/src/atom/hidden.js +5 -2
- package/src/atom/index.js +17 -9
- package/src/attribute.js +182 -100
- package/src/converter.js +68 -41
- package/src/converterFlags.js +67 -45
- package/src/converterRule.js +117 -65
- package/src/extLink.js +66 -18
- package/src/gallery.js +42 -15
- package/src/heading.js +34 -15
- package/src/html.js +97 -35
- package/src/imageParameter.js +83 -54
- package/src/index.js +299 -178
- package/src/link/category.js +20 -52
- package/src/link/file.js +59 -28
- package/src/link/galleryImage.js +21 -7
- package/src/link/index.js +146 -60
- package/src/magicLink.js +34 -12
- package/src/nowiki/comment.js +22 -10
- package/src/nowiki/dd.js +37 -22
- package/src/nowiki/doubleUnderscore.js +16 -7
- package/src/nowiki/hr.js +11 -7
- package/src/nowiki/index.js +16 -9
- package/src/nowiki/list.js +2 -2
- package/src/nowiki/noinclude.js +8 -4
- package/src/nowiki/quote.js +11 -7
- package/src/onlyinclude.js +19 -7
- package/src/parameter.js +65 -38
- package/src/syntax.js +26 -20
- package/src/table/index.js +260 -165
- package/src/table/td.js +98 -52
- package/src/table/tr.js +102 -58
- package/src/tagPair/ext.js +27 -19
- package/src/tagPair/include.js +16 -11
- package/src/tagPair/index.js +64 -29
- package/src/transclude.js +170 -93
- package/test/api.js +83 -0
- package/test/real.js +133 -0
- package/test/test.js +28 -0
- package/test/util.js +80 -0
- package/tool/index.js +41 -31
- package/typings/api.d.ts +13 -0
- package/typings/array.d.ts +28 -0
- package/typings/event.d.ts +24 -0
- package/typings/index.d.ts +46 -4
- package/typings/node.d.ts +15 -9
- package/typings/parser.d.ts +7 -0
- package/typings/tool.d.ts +3 -2
- package/util/debug.js +21 -18
- package/util/string.js +40 -27
- package/typings/element.d.ts +0 -28
package/test/real.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {diff} = require('./util'),
|
|
4
|
+
Api = require('./api'),
|
|
5
|
+
Parser = require('../'),
|
|
6
|
+
AstText = require('../lib/text');
|
|
7
|
+
|
|
8
|
+
const {argv: [,, site = '']} = process,
|
|
9
|
+
apis = [
|
|
10
|
+
['LLWiki', 'https://llwiki.org/mediawiki', 'llwiki'],
|
|
11
|
+
['萌娘百科', 'https://zh.moegirl.org.cn', 'moegirl'],
|
|
12
|
+
['维基百科', 'https://zh.wikipedia.org/w', 'zhwiki'],
|
|
13
|
+
].filter(([name]) => name.toLowerCase().includes(site.toLowerCase())),
|
|
14
|
+
complexOrHiddenTypes = [
|
|
15
|
+
// 以下为子节点必为Token的类
|
|
16
|
+
'ext',
|
|
17
|
+
'arg',
|
|
18
|
+
'template',
|
|
19
|
+
'magic-word',
|
|
20
|
+
'parameter',
|
|
21
|
+
'heading',
|
|
22
|
+
'html',
|
|
23
|
+
'table',
|
|
24
|
+
'tr',
|
|
25
|
+
'td',
|
|
26
|
+
'link',
|
|
27
|
+
'category',
|
|
28
|
+
'file',
|
|
29
|
+
'gallery-image',
|
|
30
|
+
'ext-link',
|
|
31
|
+
'converter',
|
|
32
|
+
'converter-flags',
|
|
33
|
+
'converter-rule',
|
|
34
|
+
// 以下为不可见的类
|
|
35
|
+
'noinclude',
|
|
36
|
+
'include',
|
|
37
|
+
'comment',
|
|
38
|
+
'double-underscore',
|
|
39
|
+
'hidden',
|
|
40
|
+
// 以下为SyntaxToken
|
|
41
|
+
'magic-word-name',
|
|
42
|
+
'heading-trail',
|
|
43
|
+
'table-syntax',
|
|
44
|
+
// 以下为代码不受限的NowikiToken
|
|
45
|
+
'ext-inner#nowiki',
|
|
46
|
+
'ext-inner#pre',
|
|
47
|
+
'ext-inner#syntaxhighlight',
|
|
48
|
+
'ext-inner#source',
|
|
49
|
+
'ext-inner#math',
|
|
50
|
+
'ext-inner#timeline',
|
|
51
|
+
],
|
|
52
|
+
simpleTypes = new Set([
|
|
53
|
+
'ext-inner',
|
|
54
|
+
'ext-attr',
|
|
55
|
+
'html-attr',
|
|
56
|
+
'table-attr',
|
|
57
|
+
'arg-default',
|
|
58
|
+
'parameter-key',
|
|
59
|
+
'parameter-value',
|
|
60
|
+
'heading-title',
|
|
61
|
+
'td-inner',
|
|
62
|
+
'link-target',
|
|
63
|
+
'link-text',
|
|
64
|
+
'image-parameter',
|
|
65
|
+
'ext-link-text',
|
|
66
|
+
'converter-rule-noconvert',
|
|
67
|
+
'converter-rule-to',
|
|
68
|
+
]),
|
|
69
|
+
possibleSyntax = /[{}]|\[{2,}|\[(?!(?:(?!https?\b)[^[])*\])|(?<=^|\])([^[]*?)\]+|<(?=\s*\/?\w+[\s/>])/giu;
|
|
70
|
+
|
|
71
|
+
Parser.debugging = true;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 获取最近更改的页面源代码
|
|
75
|
+
* @param {string} url api.php网址
|
|
76
|
+
*/
|
|
77
|
+
const getPages = async url => {
|
|
78
|
+
const api = new Api(url),
|
|
79
|
+
generatorParams = {generator: 'recentchanges', grcnamespace: '0|10', grclimit: 'max', grctype: 'edit'},
|
|
80
|
+
revisionParams = {prop: 'revisions', rvprop: 'contentmodel|content'};
|
|
81
|
+
/** @type {{query: {pages: MediaWikiPage[]}}} */
|
|
82
|
+
const {query: {pages}} = await api.get({...generatorParams, ...revisionParams});
|
|
83
|
+
return pages.map(({title, ns, revisions}) => ({
|
|
84
|
+
title, ns, content: revisions?.[0]?.contentmodel === 'wikitext' && revisions?.[0]?.content,
|
|
85
|
+
})).filter(({content}) => content);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
(async () => {
|
|
89
|
+
const moreTypes = new Set();
|
|
90
|
+
for (const [name, url, config] of apis) {
|
|
91
|
+
Parser.debug(`开始检查${name}:`);
|
|
92
|
+
Parser.config = `./config/${config}`;
|
|
93
|
+
try {
|
|
94
|
+
const revs = await getPages(`${url}/api.php`);
|
|
95
|
+
for (const {title, ns, content} of revs) {
|
|
96
|
+
try {
|
|
97
|
+
console.time(title);
|
|
98
|
+
const root = Parser.parse(content, ns === 10);
|
|
99
|
+
console.timeEnd(title);
|
|
100
|
+
await diff(content, String(root));
|
|
101
|
+
for (const token of root.querySelectorAll(`:not(${complexOrHiddenTypes.join()})`)) {
|
|
102
|
+
const {childNodes, type, hidden} = token;
|
|
103
|
+
if (hidden && !simpleTypes.has(type)) {
|
|
104
|
+
moreTypes.add(type);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
let first;
|
|
108
|
+
for (let i = 0; i < childNodes.length; i++) {
|
|
109
|
+
const /** @type {AstText} */ {type: childType, data} = childNodes[i];
|
|
110
|
+
if (childType === 'text') {
|
|
111
|
+
first = i;
|
|
112
|
+
if (data.search(possibleSyntax) >= 0) {
|
|
113
|
+
token.setText(data.replaceAll(possibleSyntax, '$1'), i);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (first === undefined && !simpleTypes.has(type)) {
|
|
118
|
+
moreTypes.add(type);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
await diff(content, String(root));
|
|
122
|
+
} catch (e) {
|
|
123
|
+
Parser.error(`解析${name}的 ${title} 页面时出错!`, e);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} catch (e) {
|
|
127
|
+
Parser.error(`访问${name}的API端口时出错!`, e);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (moreTypes.size > 0) {
|
|
131
|
+
Parser.debug('其他可能不含纯文本子节点的类:', moreTypes);
|
|
132
|
+
}
|
|
133
|
+
})();
|
package/test/test.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/* eslint-disable no-var */
|
|
3
|
+
const fs = require('fs/promises'),
|
|
4
|
+
assert = require('assert'), // eslint-disable-line no-unused-vars
|
|
5
|
+
path = require('path');
|
|
6
|
+
|
|
7
|
+
var Parser = require('..');
|
|
8
|
+
var wikitext = ''; // eslint-disable-line no-unused-vars
|
|
9
|
+
const {argv: [,, title = '']} = process;
|
|
10
|
+
Parser.debugging = true;
|
|
11
|
+
|
|
12
|
+
(async () => {
|
|
13
|
+
const list = await fs.readdir(path.join(__dirname, '../wiki'));
|
|
14
|
+
for (const file of list) {
|
|
15
|
+
if (file.endsWith('.md') && file.toLowerCase().includes(title.toLowerCase())) {
|
|
16
|
+
Parser.debug(file);
|
|
17
|
+
const md = await fs.readFile(path.join(__dirname, '../wiki', file), 'utf8');
|
|
18
|
+
for (const [, code] of md.matchAll(/```js\n(.+?)\n```/gsu)) {
|
|
19
|
+
try {
|
|
20
|
+
eval(code); // eslint-disable-line no-eval
|
|
21
|
+
} catch (e) {
|
|
22
|
+
Parser.error(code);
|
|
23
|
+
throw e;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})();
|
package/test/util.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const {spawn} = require('child_process'),
|
|
3
|
+
fs = require('fs/promises');
|
|
4
|
+
|
|
5
|
+
process.on('unhandledRejection', e => {
|
|
6
|
+
console.error(e);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 将shell命令转化为Promise对象
|
|
11
|
+
* @param {string} command shell指令
|
|
12
|
+
* @param {string[]} args shell输入参数
|
|
13
|
+
* @returns {Promise<?string>}
|
|
14
|
+
*/
|
|
15
|
+
const cmd = (command, args) => new Promise(resolve => {
|
|
16
|
+
let timer, shell;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 清除进程并返回
|
|
20
|
+
* @param {any} val 返回值
|
|
21
|
+
*/
|
|
22
|
+
const r = val => {
|
|
23
|
+
clearTimeout(timer);
|
|
24
|
+
shell.kill('SIGINT');
|
|
25
|
+
resolve(val);
|
|
26
|
+
};
|
|
27
|
+
try {
|
|
28
|
+
shell = spawn(command, args);
|
|
29
|
+
timer = setTimeout(() => {
|
|
30
|
+
shell.kill('SIGINT');
|
|
31
|
+
}, 60 * 1000);
|
|
32
|
+
let buf = '';
|
|
33
|
+
shell.stdout.on('data', data => {
|
|
34
|
+
buf += data.toString();
|
|
35
|
+
});
|
|
36
|
+
shell.stdout.on('end', () => {
|
|
37
|
+
r(buf);
|
|
38
|
+
});
|
|
39
|
+
shell.on('exit', () => {
|
|
40
|
+
r(shell.killed ? null : '');
|
|
41
|
+
});
|
|
42
|
+
shell.on('error', () => {
|
|
43
|
+
r(null);
|
|
44
|
+
});
|
|
45
|
+
} catch {
|
|
46
|
+
r(null);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 比较两个文件
|
|
52
|
+
* @param {string} oldfile 旧文件
|
|
53
|
+
* @param {string} newfile 新文件
|
|
54
|
+
*/
|
|
55
|
+
const diff = async (oldfile, newfile) => {
|
|
56
|
+
if (oldfile === newfile) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
await Promise.all([fs.writeFile('npmTestOldContent', oldfile), fs.writeFile('npmTestNewContent', newfile)]);
|
|
60
|
+
const stdout = await cmd('git', [
|
|
61
|
+
'diff',
|
|
62
|
+
'--color-words=[\xc0-\xff][\x80-\xbf]+|<?/?\\w+/?>?|[^[:space:]]',
|
|
63
|
+
'-U0',
|
|
64
|
+
'--no-index',
|
|
65
|
+
'npmTestOldContent',
|
|
66
|
+
'npmTestNewContent',
|
|
67
|
+
]);
|
|
68
|
+
await Promise.all([fs.unlink('npmTestOldContent'), fs.unlink('npmTestNewContent')]);
|
|
69
|
+
console.log(stdout?.split('\n')?.slice(4)?.join('\n'));
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 延时
|
|
74
|
+
* @param {number} t 秒数
|
|
75
|
+
*/
|
|
76
|
+
const sleep = t => new Promise(resolve => {
|
|
77
|
+
setTimeout(resolve, t * 1000);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
module.exports = {diff, sleep};
|
package/tool/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
/* eslint no-underscore-dangle: [2, {allowAfterThis: true, enforceInMethodNames: false, allow: ['__filename']}] */
|
|
1
2
|
'use strict';
|
|
2
3
|
|
|
3
4
|
const {typeError, externalUse} = require('../util/debug'),
|
|
4
5
|
{text, noWrap} = require('../util/string'),
|
|
6
|
+
AstText = require('../lib/text'),
|
|
5
7
|
Token = require('../src'),
|
|
6
8
|
assert = require('assert/strict');
|
|
7
9
|
|
|
@@ -9,7 +11,7 @@ const /** @type {WeakMap<Token, Record<string, any>>} */ dataStore = new WeakMap
|
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* @param {string} method
|
|
12
|
-
* @param {
|
|
14
|
+
* @param {AstText|Token|Token[]} selector
|
|
13
15
|
* @returns {(token: Token) => boolean}
|
|
14
16
|
*/
|
|
15
17
|
const matchesGenerator = (method, selector) => {
|
|
@@ -20,7 +22,7 @@ const matchesGenerator = (method, selector) => {
|
|
|
20
22
|
} else if (selector instanceof Token) {
|
|
21
23
|
return token => token === selector;
|
|
22
24
|
}
|
|
23
|
-
typeError(TokenCollection, method, 'String', 'Token', 'Array');
|
|
25
|
+
return typeError(TokenCollection, method, 'String', 'Token', 'Array'); // eslint-disable-line no-use-before-define
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
/** @extends {Array<Token>} */
|
|
@@ -37,10 +39,10 @@ class TokenCollection extends Array {
|
|
|
37
39
|
for (const token of arr) {
|
|
38
40
|
if (token === undefined) {
|
|
39
41
|
continue;
|
|
40
|
-
} else if (
|
|
42
|
+
} else if (token instanceof AstText) {
|
|
41
43
|
this.push(token);
|
|
42
44
|
} else if (!(token instanceof Token)) {
|
|
43
|
-
this.typeError('constructor', '
|
|
45
|
+
this.typeError('constructor', 'AstText', 'Token');
|
|
44
46
|
} else if (!this.includes(token)) {
|
|
45
47
|
this.#roots.add(token.getRootNode());
|
|
46
48
|
this.push(token);
|
|
@@ -58,20 +60,21 @@ class TokenCollection extends Array {
|
|
|
58
60
|
return typeError(this.constructor, method, ...types);
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
/** 节点排序 */
|
|
61
64
|
#sort() {
|
|
62
|
-
if (this.some(
|
|
65
|
+
if (this.some(({type}) => type === 'text')) {
|
|
63
66
|
return;
|
|
64
67
|
}
|
|
65
68
|
const rootArray = [...this.#roots];
|
|
66
69
|
this.sort((a, b) => {
|
|
67
70
|
const aRoot = a.getRootNode(),
|
|
68
71
|
bRoot = b.getRootNode();
|
|
69
|
-
return aRoot === bRoot ? a.
|
|
72
|
+
return aRoot === bRoot ? a.compareDocumentPosition(b) : rootArray.indexOf(aRoot) - rootArray.indexOf(bRoot);
|
|
70
73
|
});
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
toArray() {
|
|
74
|
-
return
|
|
77
|
+
return [...this];
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
_filter(selector = '') {
|
|
@@ -108,9 +111,9 @@ class TokenCollection extends Array {
|
|
|
108
111
|
|
|
109
112
|
/** @param {CollectionCallback<void, string|Token>} callback */
|
|
110
113
|
each(callback) {
|
|
111
|
-
this.
|
|
112
|
-
callback.call(
|
|
113
|
-
}
|
|
114
|
+
for (let i = 0; i < this.length; i++) {
|
|
115
|
+
callback.call(this[i], i, this[i]);
|
|
116
|
+
}
|
|
114
117
|
return this;
|
|
115
118
|
}
|
|
116
119
|
|
|
@@ -162,8 +165,9 @@ class TokenCollection extends Array {
|
|
|
162
165
|
text(str) {
|
|
163
166
|
/** @type {(ele: Token, i: number, str: string) => string} */
|
|
164
167
|
const callback = typeof str === 'function' ? str.call : () => str;
|
|
165
|
-
if (
|
|
166
|
-
for (
|
|
168
|
+
if (typeof str === 'string' || typeof str === 'function') {
|
|
169
|
+
for (let i = 0; i < this.length; i++) {
|
|
170
|
+
const ele = this[i];
|
|
167
171
|
if (ele instanceof Token) {
|
|
168
172
|
try {
|
|
169
173
|
ele.replaceChildren(callback(ele, i, ele.text()));
|
|
@@ -237,7 +241,7 @@ class TokenCollection extends Array {
|
|
|
237
241
|
} else if (selector instanceof Token) {
|
|
238
242
|
return arr.some(ele => ele.contains(selector));
|
|
239
243
|
}
|
|
240
|
-
this.typeError('has', 'String', 'Token');
|
|
244
|
+
return this.typeError('has', 'String', 'Token');
|
|
241
245
|
}
|
|
242
246
|
|
|
243
247
|
/** @param {string} selector */
|
|
@@ -292,11 +296,11 @@ class TokenCollection extends Array {
|
|
|
292
296
|
*/
|
|
293
297
|
_siblings(start, count, selector = '') {
|
|
294
298
|
return this._create(arr => arr.flatMap(ele => {
|
|
295
|
-
const {
|
|
296
|
-
if (!
|
|
299
|
+
const {parentNode} = ele;
|
|
300
|
+
if (!parentNode) {
|
|
297
301
|
return undefined;
|
|
298
302
|
}
|
|
299
|
-
const {children} =
|
|
303
|
+
const {children} = parentNode,
|
|
300
304
|
i = children.indexOf(ele);
|
|
301
305
|
children.splice(
|
|
302
306
|
typeof start === 'function' ? start(i) : start,
|
|
@@ -323,7 +327,7 @@ class TokenCollection extends Array {
|
|
|
323
327
|
* @param {string|Token|Token[]} selector
|
|
324
328
|
*/
|
|
325
329
|
_until(method, selector, filter = '') {
|
|
326
|
-
const matches = matchesGenerator(`${method.replace(/All
|
|
330
|
+
const matches = matchesGenerator(`${method.replace(/All$/u, '')}Until`, selector);
|
|
327
331
|
return this._create(arr => arr.flatMap(ele => {
|
|
328
332
|
const tokens = $(ele)[method]().toArray(),
|
|
329
333
|
tokenArray = method === 'nextAll' ? tokens : tokens.reverse(),
|
|
@@ -382,7 +386,7 @@ class TokenCollection extends Array {
|
|
|
382
386
|
if (name !== undefined && typeof name !== 'string' && !Array.isArray(name)) {
|
|
383
387
|
this.typeError('removeData', 'String', 'Array');
|
|
384
388
|
}
|
|
385
|
-
name = typeof name === 'string' ? name.split(/\s/) : name;
|
|
389
|
+
name = typeof name === 'string' ? name.split(/\s/u) : name;
|
|
386
390
|
for (const token of this._filter()) {
|
|
387
391
|
if (!$.dataStore.has(token)) {
|
|
388
392
|
continue;
|
|
@@ -404,14 +408,14 @@ class TokenCollection extends Array {
|
|
|
404
408
|
* @param {AstListener} handler
|
|
405
409
|
*/
|
|
406
410
|
_addEventListener(events, selector, handler, once = false) {
|
|
407
|
-
if (
|
|
411
|
+
if (typeof events !== 'string' && typeof events !== 'object') {
|
|
408
412
|
this.typeError(once ? 'once' : 'on', 'String', 'Object');
|
|
409
413
|
} else if (typeof selector === 'function') {
|
|
410
414
|
handler = selector;
|
|
411
415
|
selector = undefined;
|
|
412
416
|
}
|
|
413
417
|
const eventPair = typeof events === 'string'
|
|
414
|
-
? events.split(/\s/).map(/** @returns {[string, AstListener]} */ event => [event, handler])
|
|
418
|
+
? events.split(/\s/u).map(/** @returns {[string, AstListener]} */ event => [event, handler])
|
|
415
419
|
: Object.entries(events);
|
|
416
420
|
for (const token of this._filter(selector)) {
|
|
417
421
|
for (const [event, listener] of eventPair) {
|
|
@@ -445,14 +449,14 @@ class TokenCollection extends Array {
|
|
|
445
449
|
* @param {AstListener} handler
|
|
446
450
|
*/
|
|
447
451
|
off(events, selector, handler) {
|
|
448
|
-
if (
|
|
452
|
+
if (typeof events !== 'string' && typeof events !== 'object' && events !== undefined) {
|
|
449
453
|
this.typeError('off', 'String', 'Object');
|
|
450
454
|
}
|
|
451
455
|
handler = typeof selector === 'function' ? selector : handler;
|
|
452
456
|
let eventPair;
|
|
453
457
|
if (events) {
|
|
454
458
|
eventPair = typeof events === 'string'
|
|
455
|
-
? events.split(/\s/).map(/** @returns {[string, AstListener]} */ event => [event, handler])
|
|
459
|
+
? events.split(/\s/u).map(/** @returns {[string, AstListener]} */ event => [event, handler])
|
|
456
460
|
: Object.entries(events);
|
|
457
461
|
}
|
|
458
462
|
for (const token of this._filter(selector)) {
|
|
@@ -460,7 +464,7 @@ class TokenCollection extends Array {
|
|
|
460
464
|
token.removeAllEventListeners();
|
|
461
465
|
} else {
|
|
462
466
|
for (const [event, listener] of eventPair) {
|
|
463
|
-
if (typeof event !== 'string' ||
|
|
467
|
+
if (typeof event !== 'string' || typeof listener !== 'function' && listener !== undefined) {
|
|
464
468
|
this.typeError('off', 'String', 'Function');
|
|
465
469
|
} else if (listener) {
|
|
466
470
|
token.removeEventListener(event, listener);
|
|
@@ -486,7 +490,7 @@ class TokenCollection extends Array {
|
|
|
486
490
|
triggerHandler(event, data) {
|
|
487
491
|
const firstToken = this._find();
|
|
488
492
|
if (!firstToken) {
|
|
489
|
-
return;
|
|
493
|
+
return undefined;
|
|
490
494
|
}
|
|
491
495
|
const e = typeof event === 'string' ? new Event(event) : event,
|
|
492
496
|
listeners = firstToken.listEventListeners(typeof event === 'string' ? event : event.type);
|
|
@@ -504,7 +508,8 @@ class TokenCollection extends Array {
|
|
|
504
508
|
*/
|
|
505
509
|
_insert(method, content, ...additional) {
|
|
506
510
|
if (typeof content === 'function') {
|
|
507
|
-
for (
|
|
511
|
+
for (let i = 0; i < this.length; i++) {
|
|
512
|
+
const token = this[i];
|
|
508
513
|
if (token instanceof Token) {
|
|
509
514
|
const result = content.call(token, i, token.toString());
|
|
510
515
|
if (typeof result === 'string' || result instanceof Token) {
|
|
@@ -576,7 +581,8 @@ class TokenCollection extends Array {
|
|
|
576
581
|
}
|
|
577
582
|
|
|
578
583
|
remove(selector = '') {
|
|
579
|
-
|
|
584
|
+
this.removeData();
|
|
585
|
+
for (const token of this._filter(selector)) {
|
|
580
586
|
token.remove();
|
|
581
587
|
token.removeAllEventListeners();
|
|
582
588
|
}
|
|
@@ -659,7 +665,8 @@ class TokenCollection extends Array {
|
|
|
659
665
|
} else {
|
|
660
666
|
this.typeError('val', 'String', 'Array', 'Function');
|
|
661
667
|
}
|
|
662
|
-
for (
|
|
668
|
+
for (let i = 0; i < this.length; i++) {
|
|
669
|
+
const token = this[i];
|
|
663
670
|
if (token instanceof Token && typeof token.setValue === 'function' && token.setValue.length === 1) {
|
|
664
671
|
token.setValue(toValue(i, token));
|
|
665
672
|
}
|
|
@@ -678,7 +685,8 @@ class TokenCollection extends Array {
|
|
|
678
685
|
const firstToken = this._find();
|
|
679
686
|
return firstToken?.[getter] && firstToken[getter](name);
|
|
680
687
|
}
|
|
681
|
-
for (
|
|
688
|
+
for (let i = 0; i < this.length; i++) {
|
|
689
|
+
const token = this[i];
|
|
682
690
|
if (token instanceof Token && typeof token[setter] === 'function') {
|
|
683
691
|
if (typeof value === 'string') {
|
|
684
692
|
token[setter](name, value);
|
|
@@ -784,6 +792,7 @@ class TokenCollection extends Array {
|
|
|
784
792
|
this.typeError(method, 'Array', 'Function');
|
|
785
793
|
}
|
|
786
794
|
return this[method](
|
|
795
|
+
|
|
787
796
|
/**
|
|
788
797
|
* @this {string|Token}
|
|
789
798
|
* @param {number} i
|
|
@@ -817,7 +826,7 @@ class TokenCollection extends Array {
|
|
|
817
826
|
offset() {
|
|
818
827
|
const firstToken = this._find();
|
|
819
828
|
if (!firstToken) {
|
|
820
|
-
return;
|
|
829
|
+
return undefined;
|
|
821
830
|
}
|
|
822
831
|
const {top, left} = firstToken.getBoundingClientRect();
|
|
823
832
|
return {top, left};
|
|
@@ -837,9 +846,9 @@ class TokenCollection extends Array {
|
|
|
837
846
|
}
|
|
838
847
|
}
|
|
839
848
|
|
|
840
|
-
/** @param {
|
|
849
|
+
/** @param {AstText|Token|Iterable<string|Token>} tokens */
|
|
841
850
|
const $ = tokens => {
|
|
842
|
-
if (
|
|
851
|
+
if (tokens instanceof AstText || tokens instanceof Token) {
|
|
843
852
|
tokens = [tokens];
|
|
844
853
|
}
|
|
845
854
|
return new Proxy(new TokenCollection(...tokens), {
|
|
@@ -851,6 +860,7 @@ const $ = tokens => {
|
|
|
851
860
|
) {
|
|
852
861
|
return obj[prop];
|
|
853
862
|
}
|
|
863
|
+
return undefined;
|
|
854
864
|
},
|
|
855
865
|
set(obj, prop, val) {
|
|
856
866
|
if (prop === 'prevObject' && (val === undefined || val instanceof TokenCollection)) {
|
package/typings/api.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
interface Array<T> {
|
|
3
|
+
/**
|
|
4
|
+
* Returns the value of the last element in the array where predicate is true, and undefined
|
|
5
|
+
* otherwise.
|
|
6
|
+
* @param predicate find calls predicate once for each element of the array, in descending
|
|
7
|
+
* order, until it finds one where predicate returns true. If such an element is found, findLast
|
|
8
|
+
* immediately returns that element value. Otherwise, findLast returns undefined.
|
|
9
|
+
* @param thisArg If provided, it will be used as the this value for each invocation of
|
|
10
|
+
* predicate. If it is not provided, undefined is used instead.
|
|
11
|
+
*/
|
|
12
|
+
findLast<S extends T>(predicate: (this: void, value: T, index: number, obj: T[]) => value is S, thisArg?: any): S | undefined;
|
|
13
|
+
findLast(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): T | undefined;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns the index of the last element in the array where predicate is true, and -1
|
|
17
|
+
* otherwise.
|
|
18
|
+
* @param predicate find calls predicate once for each element of the array, in descending
|
|
19
|
+
* order, until it finds one where predicate returns true. If such an element is found,
|
|
20
|
+
* findLastIndex immediately returns that element index. Otherwise, findLastIndex returns -1.
|
|
21
|
+
* @param thisArg If provided, it will be used as the this value for each invocation of
|
|
22
|
+
* predicate. If it is not provided, undefined is used instead.
|
|
23
|
+
*/
|
|
24
|
+
findLastIndex(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): number;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import Token from '../src';
|
|
2
|
+
import AstText from '../lib/text';
|
|
3
|
+
|
|
4
|
+
declare global {
|
|
5
|
+
interface AstEvent extends Event {
|
|
6
|
+
readonly target: Token & AstText;
|
|
7
|
+
currentTarget: Token;
|
|
8
|
+
prevTarget: ?Token;
|
|
9
|
+
};
|
|
10
|
+
interface AstEventData {
|
|
11
|
+
position: number;
|
|
12
|
+
removed: AstText|Token;
|
|
13
|
+
inserted: AstText|Token;
|
|
14
|
+
oldToken: Token;
|
|
15
|
+
newToken: Token;
|
|
16
|
+
oldText: string;
|
|
17
|
+
newText: string;
|
|
18
|
+
oldKey: string;
|
|
19
|
+
newKey: string;
|
|
20
|
+
}
|
|
21
|
+
type AstListener = (e: AstEvent, data: AstEventData) => any;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export {};
|
package/typings/index.d.ts
CHANGED
|
@@ -7,13 +7,29 @@ declare global {
|
|
|
7
7
|
warning: boolean;
|
|
8
8
|
debugging: boolean;
|
|
9
9
|
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* 默认输出到console.warn
|
|
12
|
+
* @param {string} msg 消息
|
|
13
|
+
* @param {...any} args 更多信息
|
|
14
|
+
*/
|
|
11
15
|
warn(msg: string, ...args: any[]): void;
|
|
12
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* 默认不输出到console.debug
|
|
18
|
+
* @param {string} msg 消息
|
|
19
|
+
* @param {...any} args 更多信息
|
|
20
|
+
*/
|
|
13
21
|
debug(msg: string, ...args: any[]): void;
|
|
14
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* 总是输出到console.error
|
|
24
|
+
* @param {string} msg 消息
|
|
25
|
+
* @param {...any} args 更多信息
|
|
26
|
+
*/
|
|
15
27
|
error(msg: string, ...args: any[]): void;
|
|
16
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* 总是输出到console.info
|
|
30
|
+
* @param {string} msg 消息
|
|
31
|
+
* @param {...any} args 更多信息
|
|
32
|
+
*/
|
|
17
33
|
info(msg: string, ...args: any[]): void;
|
|
18
34
|
|
|
19
35
|
running: boolean;
|
|
@@ -27,20 +43,46 @@ declare global {
|
|
|
27
43
|
/** 清除各模块的缓存 */
|
|
28
44
|
clearCache(): void;
|
|
29
45
|
|
|
46
|
+
/**
|
|
47
|
+
* 打印函数定义
|
|
48
|
+
* @param {Function} f 待打印的函数
|
|
49
|
+
*/
|
|
30
50
|
log(f: Function): void;
|
|
31
51
|
|
|
32
52
|
readonly aliases: string[][];
|
|
33
53
|
|
|
34
54
|
config: string;
|
|
55
|
+
/** 获取设置 */
|
|
35
56
|
getConfig(): ParserConfig;
|
|
36
57
|
|
|
58
|
+
/**
|
|
59
|
+
* 是否是跨维基链接
|
|
60
|
+
* @param {string} title 链接标题
|
|
61
|
+
*/
|
|
37
62
|
isInterwiki(title: string, config?: ParserConfig): RegExpMatchArray;
|
|
63
|
+
/**
|
|
64
|
+
* 规范化页面标题
|
|
65
|
+
* @param {string} title 标题(含或不含命名空间前缀)
|
|
66
|
+
* @param {number} defaultNs 命名空间
|
|
67
|
+
* @param {boolean} include 是否嵌入
|
|
68
|
+
* @param {boolean} halfParsed 是否是半解析状态
|
|
69
|
+
*/
|
|
38
70
|
normalizeTitle(
|
|
39
71
|
title: string, defaultNs?: number, include?: boolean, config?: ParserConfig, halfParsed?: boolean
|
|
40
72
|
): Title;
|
|
41
73
|
|
|
42
74
|
readonly MAX_STAGE: number;
|
|
75
|
+
/**
|
|
76
|
+
* 解析wikitext
|
|
77
|
+
* @param {string|Token} wikitext wikitext
|
|
78
|
+
* @param {boolean} include 是否嵌入
|
|
79
|
+
* @param {number} maxStage 最大解析层级
|
|
80
|
+
*/
|
|
43
81
|
parse(wikitext: string|Token, include?: boolean, maxStage?: number, config?: ParserConfig): Token;
|
|
82
|
+
/**
|
|
83
|
+
* 再次解析上次出错的wikitext
|
|
84
|
+
* @param {string} date 错误日期
|
|
85
|
+
*/
|
|
44
86
|
reparse(date: string): Token;
|
|
45
87
|
|
|
46
88
|
getTool(): typeof $;
|
package/typings/node.d.ts
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
import Ranges from '../lib/ranges';
|
|
2
2
|
import Token from '../src';
|
|
3
|
+
import AstText from '../lib/text';
|
|
3
4
|
import ParameterToken from '../src/parameter';
|
|
4
5
|
|
|
5
6
|
declare global {
|
|
6
7
|
type TokenAttribute<T> =
|
|
7
|
-
T extends '
|
|
8
|
-
T extends '
|
|
8
|
+
T extends 'stage' ? number :
|
|
9
|
+
T extends 'include' ? boolean :
|
|
10
|
+
T extends 'pattern' ? RegExp :
|
|
9
11
|
T extends 'optional'|'tags'|'flags' ? string[] :
|
|
10
|
-
T extends '
|
|
12
|
+
T extends 'keys' ? Set<string> :
|
|
13
|
+
T extends 'attr' ? Map<string, string|true> :
|
|
14
|
+
T extends 'acceptable' ? Record<string, Ranges> :
|
|
15
|
+
T extends 'args' ? Record<string, Set<ParameterToken>> :
|
|
11
16
|
T extends 'config' ? ParserConfig :
|
|
12
17
|
T extends 'accum' ? accum :
|
|
13
|
-
T extends 'acceptable' ? Record<string, Ranges> :
|
|
14
18
|
T extends 'protectedChildren' ? Ranges :
|
|
15
|
-
T extends '
|
|
16
|
-
T extends '
|
|
17
|
-
T extends '
|
|
18
|
-
T extends '
|
|
19
|
-
T extends '
|
|
19
|
+
T extends 'parentNode' ? Token|undefined :
|
|
20
|
+
T extends 'childNodes' ? (AstText|Token)[] :
|
|
21
|
+
T extends 'verifyChild' ? (i: number, addition: number) => void :
|
|
22
|
+
T extends 'matchesAttr' ? (key: string, equal: string, val: string, i: string) => boolean :
|
|
23
|
+
T extends 'parseOnce' ? (n: number, include: boolean) => Token :
|
|
24
|
+
T extends 'buildFromStr' ? (str: string) => (AstText|Token)[] :
|
|
25
|
+
T extends 'protectChildren' ? (...args: string|number|Range) => void :
|
|
20
26
|
string;
|
|
21
27
|
}
|
|
22
28
|
|