wikiparser-node 0.3.1 → 0.5.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/README.md +1 -1
- package/config/default.json +13 -17
- package/config/llwiki.json +11 -79
- package/config/moegirl.json +7 -1
- package/config/zhwiki.json +1269 -0
- package/index.js +130 -97
- package/lib/element.js +410 -518
- package/lib/node.js +493 -115
- package/lib/ranges.js +27 -19
- package/lib/text.js +175 -0
- package/lib/title.js +14 -6
- package/mixin/attributeParent.js +70 -24
- package/mixin/fixedToken.js +18 -10
- package/mixin/hidden.js +6 -4
- package/mixin/sol.js +39 -12
- package/package.json +17 -4
- package/parser/brackets.js +18 -18
- package/parser/commentAndExt.js +16 -14
- package/parser/converter.js +14 -13
- package/parser/externalLinks.js +12 -11
- package/parser/hrAndDoubleUnderscore.js +24 -14
- package/parser/html.js +8 -7
- package/parser/links.js +13 -13
- package/parser/list.js +12 -11
- package/parser/magicLinks.js +11 -10
- package/parser/quotes.js +6 -5
- package/parser/selector.js +175 -0
- package/parser/table.js +31 -24
- package/src/arg.js +91 -43
- package/src/atom/hidden.js +5 -2
- package/src/atom/index.js +17 -9
- package/src/attribute.js +210 -101
- package/src/converter.js +78 -43
- package/src/converterFlags.js +104 -45
- package/src/converterRule.js +136 -78
- package/src/extLink.js +81 -27
- package/src/gallery.js +63 -20
- package/src/heading.js +58 -20
- package/src/html.js +138 -48
- package/src/imageParameter.js +93 -58
- package/src/index.js +314 -186
- package/src/link/category.js +22 -54
- package/src/link/file.js +83 -32
- package/src/link/galleryImage.js +21 -7
- package/src/link/index.js +170 -81
- package/src/magicLink.js +64 -14
- package/src/nowiki/comment.js +36 -10
- package/src/nowiki/dd.js +37 -22
- package/src/nowiki/doubleUnderscore.js +21 -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 +38 -7
- package/src/onlyinclude.js +24 -7
- package/src/parameter.js +102 -62
- package/src/syntax.js +23 -20
- package/src/table/index.js +282 -174
- package/src/table/td.js +112 -61
- package/src/table/tr.js +135 -74
- package/src/tagPair/ext.js +30 -23
- package/src/tagPair/include.js +26 -11
- package/src/tagPair/index.js +72 -29
- package/src/transclude.js +235 -127
- package/tool/index.js +42 -32
- package/util/debug.js +21 -18
- package/util/diff.js +76 -0
- package/util/lint.js +40 -0
- package/util/string.js +56 -26
- package/.eslintrc.json +0 -319
- package/errors/README +0 -1
- package/jsconfig.json +0 -7
- package/printed/README +0 -1
- package/typings/element.d.ts +0 -28
- package/typings/index.d.ts +0 -52
- package/typings/node.d.ts +0 -23
- package/typings/parser.d.ts +0 -9
- package/typings/table.d.ts +0 -14
- package/typings/token.d.ts +0 -22
- package/typings/tool.d.ts +0 -10
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,20 +846,21 @@ 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), {
|
|
846
855
|
/** @param {PropertyKey} prop */
|
|
847
856
|
get(obj, prop) {
|
|
848
857
|
if (prop === Symbol.iterator || typeof obj[prop] !== 'function'
|
|
849
|
-
||
|
|
858
|
+
|| prop[0] !== '_' && Object.getOwnPropertyDescriptor(obj.constructor.prototype, prop)
|
|
850
859
|
|| !externalUse(prop, true)
|
|
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/util/debug.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* @param {
|
|
6
|
-
* @param {
|
|
4
|
+
* 定制TypeError消息
|
|
5
|
+
* @param {Function} constructor 类
|
|
6
|
+
* @param {string} method 方法名称
|
|
7
|
+
* @param {...string} args 可接受的参数类型
|
|
8
|
+
* @throws `TypeError`
|
|
7
9
|
*/
|
|
8
10
|
const typeError = ({name}, method, ...args) => {
|
|
9
11
|
throw new TypeError(`${name}.${method} 方法仅接受 ${args.join('、')} 作为输入参数!`);
|
|
@@ -11,28 +13,30 @@ const typeError = ({name}, method, ...args) => {
|
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* 不是被构造器或原型方法调用
|
|
14
|
-
* @param {string} name
|
|
16
|
+
* @param {string} name 方法名称
|
|
15
17
|
*/
|
|
16
|
-
const externalUse =
|
|
17
|
-
|
|
18
|
+
const externalUse = name => {
|
|
19
|
+
const Parser = require('..');
|
|
20
|
+
if (Parser.running) {
|
|
18
21
|
return false;
|
|
19
22
|
}
|
|
20
|
-
const regex = RegExp(
|
|
21
|
-
proxy ? 'Proxy' : 'new \\w*Token$|^(?:AstNode|AstElement|\\w*Token)'
|
|
22
|
-
}\\.(?!${name}$)`);
|
|
23
|
+
const regex = new RegExp(`^new \\w*Token$|^(?:Ast\\w*|\\w*Token)\\.(?!${name}$)`, 'u');
|
|
23
24
|
try {
|
|
24
|
-
throw new Error();
|
|
25
|
+
throw new Error(); // eslint-disable-line unicorn/error-message
|
|
25
26
|
} catch (e) {
|
|
26
27
|
if (e instanceof Error) {
|
|
27
|
-
const mt = e.stack.match(/(?<=^\s+at )(?:new )?[\w.]+(?= \(\/)/
|
|
28
|
+
const mt = e.stack.match(/(?<=^\s+at )(?:new )?[\w.]+(?= \(\/)/gmu);
|
|
28
29
|
return !mt.slice(2).some(func => regex.test(func));
|
|
29
30
|
}
|
|
30
31
|
}
|
|
32
|
+
return false;
|
|
31
33
|
};
|
|
32
34
|
|
|
33
35
|
/**
|
|
34
|
-
*
|
|
35
|
-
* @param {
|
|
36
|
+
* 撤销最近一次Mutation
|
|
37
|
+
* @param {AstEvent} e 事件
|
|
38
|
+
* @param {AstEventData} data 事件数据
|
|
39
|
+
* @throws `RangeError` 无法撤销的事件类型
|
|
36
40
|
*/
|
|
37
41
|
const undo = (e, data) => {
|
|
38
42
|
const {target, type} = e;
|
|
@@ -40,6 +44,7 @@ const undo = (e, data) => {
|
|
|
40
44
|
case 'remove': {
|
|
41
45
|
const childNodes = [...target.childNodes];
|
|
42
46
|
childNodes.splice(data.position, 0, data.removed);
|
|
47
|
+
data.removed.setAttribute('parentNode', target);
|
|
43
48
|
target.setAttribute('childNodes', childNodes);
|
|
44
49
|
break;
|
|
45
50
|
}
|
|
@@ -53,15 +58,13 @@ const undo = (e, data) => {
|
|
|
53
58
|
const {parentNode} = target,
|
|
54
59
|
childNodes = [...parentNode.childNodes];
|
|
55
60
|
childNodes.splice(data.position, 1, data.oldToken);
|
|
61
|
+
data.oldToken.setAttribute('parentNode', parentNode);
|
|
56
62
|
parentNode.setAttribute('childNodes', childNodes);
|
|
57
63
|
break;
|
|
58
64
|
}
|
|
59
|
-
case 'text':
|
|
60
|
-
|
|
61
|
-
childNodes[data.position] = data.oldText;
|
|
62
|
-
target.setAttribute('childNodes', childNodes);
|
|
65
|
+
case 'text':
|
|
66
|
+
target.replaceData(data.oldText);
|
|
63
67
|
break;
|
|
64
|
-
}
|
|
65
68
|
default:
|
|
66
69
|
throw new RangeError(`无法撤销未知类型的事件:${type}`);
|
|
67
70
|
}
|
package/util/diff.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {spawn} = require('child_process'),
|
|
4
|
+
fs = require('fs/promises');
|
|
5
|
+
|
|
6
|
+
process.on('unhandledRejection', e => {
|
|
7
|
+
console.error(e);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 将shell命令转化为Promise对象
|
|
12
|
+
* @param {string} command shell指令
|
|
13
|
+
* @param {string[]} args shell输入参数
|
|
14
|
+
* @returns {Promise<?string>}
|
|
15
|
+
*/
|
|
16
|
+
const cmd = (command, args) => new Promise(resolve => {
|
|
17
|
+
let timer, shell;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 清除进程并返回
|
|
21
|
+
* @param {any} val 返回值
|
|
22
|
+
*/
|
|
23
|
+
const r = val => {
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
shell.kill('SIGINT');
|
|
26
|
+
resolve(val);
|
|
27
|
+
};
|
|
28
|
+
try {
|
|
29
|
+
shell = spawn(command, args);
|
|
30
|
+
timer = setTimeout(() => {
|
|
31
|
+
shell.kill('SIGINT');
|
|
32
|
+
}, 60 * 1000);
|
|
33
|
+
let buf = '';
|
|
34
|
+
shell.stdout.on('data', data => {
|
|
35
|
+
buf += data.toString();
|
|
36
|
+
});
|
|
37
|
+
shell.stdout.on('end', () => {
|
|
38
|
+
r(buf);
|
|
39
|
+
});
|
|
40
|
+
shell.on('exit', () => {
|
|
41
|
+
r(shell.killed ? null : '');
|
|
42
|
+
});
|
|
43
|
+
shell.on('error', () => {
|
|
44
|
+
r(null);
|
|
45
|
+
});
|
|
46
|
+
} catch {
|
|
47
|
+
r(null);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 比较两个文件
|
|
53
|
+
* @param {string} oldStr 旧文本
|
|
54
|
+
* @param {string} newStr 新文本
|
|
55
|
+
* @param {string} uid 唯一标识
|
|
56
|
+
*/
|
|
57
|
+
const diff = async (oldStr, newStr, uid = '') => {
|
|
58
|
+
if (oldStr === newStr) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const oldFile = `diffOld${uid}`,
|
|
62
|
+
newFile = `diffNew${uid}`;
|
|
63
|
+
await Promise.all([fs.writeFile(oldFile, oldStr), fs.writeFile(newFile, newStr)]);
|
|
64
|
+
const stdout = await cmd('git', [
|
|
65
|
+
'diff',
|
|
66
|
+
'--color-words=[\xC0-\xFF][\x80-\xBF]+|<?/?\\w+/?>?|[^[:space:]]',
|
|
67
|
+
'-U0',
|
|
68
|
+
'--no-index',
|
|
69
|
+
oldFile,
|
|
70
|
+
newFile,
|
|
71
|
+
]);
|
|
72
|
+
await Promise.all([fs.unlink(oldFile), fs.unlink(newFile)]);
|
|
73
|
+
console.log(stdout?.split('\n')?.slice(4)?.join('\n'));
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
module.exports = diff;
|
package/util/lint.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Token = require('../src');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 生成对于子节点的LintError对象
|
|
7
|
+
* @param {Token} child 子节点
|
|
8
|
+
* @param {{top: number, left: number}} boundingRect 父节点的绝对定位
|
|
9
|
+
* @param {string} message 错误信息
|
|
10
|
+
* @param {'error'|'warning'} severity 严重程度
|
|
11
|
+
* @returns {LintError}
|
|
12
|
+
*/
|
|
13
|
+
const generateForChild = (child, boundingRect, message, severity = 'error') => {
|
|
14
|
+
const {style: {top: offsetTop, left: offsetLeft, height, width}} = child,
|
|
15
|
+
{top, left} = boundingRect,
|
|
16
|
+
startLine = top + offsetTop,
|
|
17
|
+
endLine = startLine + height - 1,
|
|
18
|
+
startCol = offsetTop ? offsetLeft : left + offsetLeft,
|
|
19
|
+
endCol = height > 1 ? width : startCol + width;
|
|
20
|
+
return {message, severity, startLine, endLine, startCol, endCol};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 生成对于自己的LintError对象
|
|
25
|
+
* @param {Token} token 节点
|
|
26
|
+
* @param {{top: number, left: number}} boundingRect 绝对定位
|
|
27
|
+
* @param {string} message 错误信息
|
|
28
|
+
* @param {'error'|'warning'} severity 严重程度
|
|
29
|
+
* @returns {LintError}
|
|
30
|
+
*/
|
|
31
|
+
const generateForSelf = (token, boundingRect, message, severity = 'error') => ({
|
|
32
|
+
message,
|
|
33
|
+
severity,
|
|
34
|
+
startLine: boundingRect.top,
|
|
35
|
+
endLine: boundingRect.top + token.offsetHeight - 1,
|
|
36
|
+
startCol: boundingRect.left,
|
|
37
|
+
endCol: token.offsetHeight > 1 ? token.offsetWidth : boundingRect.left + token.offsetWidth,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
module.exports = {generateForChild, generateForSelf};
|
package/util/string.js
CHANGED
|
@@ -2,40 +2,62 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* optionally convert to lower cases
|
|
5
|
-
* @param {string} val
|
|
6
|
-
* @param {string|undefined} i
|
|
5
|
+
* @param {string} val 属性值
|
|
6
|
+
* @param {string|undefined} i 是否对大小写不敏感
|
|
7
7
|
*/
|
|
8
8
|
const toCase = (val, i) => i ? val.toLowerCase() : val;
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* remove half-parsed comment-like tokens
|
|
12
|
-
* @param {string} str
|
|
12
|
+
* @param {string} str 原字符串
|
|
13
13
|
*/
|
|
14
|
-
const removeComment = str => str.
|
|
14
|
+
const removeComment = str => str.replaceAll(/\0\d+c\x7F/gu, '');
|
|
15
15
|
|
|
16
|
-
/**
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* 以HTML格式打印
|
|
18
|
+
* @param {(AstText|AstElement)[]} childNodes 子节点
|
|
19
|
+
* @param {printOpt} opt 选项
|
|
20
|
+
*/
|
|
21
|
+
const print = (childNodes, opt = {}) => {
|
|
22
|
+
const AstText = require('../lib/text'),
|
|
23
|
+
AstElement = require('../lib/element');
|
|
24
|
+
const {pre = '', post = '', sep = ''} = opt,
|
|
25
|
+
entities = {'&': 'amp', '<': 'lt', '>': 'gt'};
|
|
26
|
+
return `${pre}${childNodes.map(
|
|
27
|
+
child => child instanceof AstElement
|
|
28
|
+
? child.print()
|
|
29
|
+
: String(child).replaceAll(/[&<>]/gu, p => `&${entities[p]};`),
|
|
30
|
+
).join(sep)}${post}`;
|
|
31
|
+
};
|
|
18
32
|
|
|
19
|
-
/**
|
|
20
|
-
|
|
33
|
+
/**
|
|
34
|
+
* escape special chars for RegExp constructor
|
|
35
|
+
* @param {string} str RegExp source
|
|
36
|
+
*/
|
|
37
|
+
const escapeRegExp = str => str.replaceAll(/[\\{}()|.?*+^$[\]]/gu, '\\$&');
|
|
21
38
|
|
|
22
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* extract effective wikitext
|
|
41
|
+
* @param {(string|AstNode)[]} childNodes a Token's contents
|
|
42
|
+
* @param {string} separator delimiter between nodes
|
|
43
|
+
*/
|
|
23
44
|
const text = (childNodes, separator = '') => {
|
|
24
45
|
const AstNode = require('../lib/node');
|
|
25
46
|
return childNodes.map(child => typeof child === 'string' ? child : child.text()).join(separator);
|
|
26
47
|
};
|
|
27
48
|
|
|
28
49
|
/**
|
|
29
|
-
*
|
|
30
|
-
* @param {string}
|
|
31
|
-
* @param {string}
|
|
32
|
-
* @param {string}
|
|
50
|
+
* a more sophisticated string-explode function
|
|
51
|
+
* @param {string} start start syntax of a nested AST node
|
|
52
|
+
* @param {string} end end syntax of a nested AST node
|
|
53
|
+
* @param {string} separator syntax for explosion
|
|
54
|
+
* @param {string} str string to be exploded
|
|
33
55
|
*/
|
|
34
56
|
const explode = (start, end, separator, str) => {
|
|
35
57
|
if (str === undefined) {
|
|
36
58
|
return [];
|
|
37
59
|
}
|
|
38
|
-
const regex = RegExp(`${[start, end, separator].map(escapeRegExp).join('|')}`, '
|
|
60
|
+
const regex = new RegExp(`${[start, end, separator].map(escapeRegExp).join('|')}`, 'gu'),
|
|
39
61
|
/** @type {string[]} */ exploded = [];
|
|
40
62
|
let mt = regex.exec(str),
|
|
41
63
|
depth = 0,
|
|
@@ -54,22 +76,30 @@ const explode = (start, end, separator, str) => {
|
|
|
54
76
|
return exploded;
|
|
55
77
|
};
|
|
56
78
|
|
|
57
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* escape newlines
|
|
81
|
+
* @param {string} str 原字符串
|
|
82
|
+
*/
|
|
58
83
|
const noWrap = str => str.replaceAll('\n', '\\n');
|
|
59
84
|
|
|
60
85
|
/**
|
|
61
|
-
*
|
|
62
|
-
* @
|
|
86
|
+
* convert newline in text nodes to single whitespace
|
|
87
|
+
* @param {Token & {childNodes: AstText[]}} token 父节点
|
|
63
88
|
*/
|
|
64
|
-
const normalizeSpace =
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
89
|
+
const normalizeSpace = token => {
|
|
90
|
+
if (token === undefined) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const Token = require('../src'),
|
|
94
|
+
AstText = require('../lib/text');
|
|
95
|
+
for (const child of token.childNodes) {
|
|
96
|
+
if (child.type === 'text') {
|
|
97
|
+
child.replaceData(child.data.replaceAll('\n', ' '));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
70
100
|
};
|
|
71
101
|
|
|
72
|
-
const extUrlChar = '(?:\\[[\\da-f:.]+\\]|[^[\\]<>"\\0-\\
|
|
73
|
-
+ '(?:[^[\\]<>"\\0-\\
|
|
102
|
+
const extUrlChar = '(?:\\[[\\da-f:.]+\\]|[^[\\]<>"\\0-\\x1F\\x7F\\p{Zs}\\uFFFD])'
|
|
103
|
+
+ '(?:[^[\\]<>"\\0-\\x1F\\x7F\\p{Zs}\\uFFFD]|\\0\\d+c\\x7F)*';
|
|
74
104
|
|
|
75
|
-
module.exports = {toCase, removeComment,
|
|
105
|
+
module.exports = {toCase, removeComment, print, escapeRegExp, text, explode, noWrap, normalizeSpace, extUrlChar};
|