wikiparser-node 0.0.1 → 0.1.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 +70 -25
- package/config/default.json +1 -0
- package/config/llwiki.json +1 -0
- package/config/moegirl.json +1 -0
- package/errors/README +1 -0
- package/index.js +43 -9
- package/lib/element.js +70 -12
- package/lib/node.js +17 -9
- package/mixin/sol.js +38 -0
- package/package.json +1 -1
- package/parser/externalLinks.js +1 -1
- package/parser/list.js +58 -0
- package/parser/table.js +2 -2
- package/printed/README +1 -0
- package/src/arg.js +3 -3
- package/src/attribute.js +31 -21
- package/src/extLink.js +19 -9
- package/src/heading.js +15 -20
- package/src/html.js +4 -3
- package/src/imageParameter.js +64 -10
- package/src/index.js +25 -14
- package/src/link/file.js +39 -7
- package/src/link/index.js +2 -4
- package/src/magicLink.js +10 -4
- package/src/nowiki/comment.js +1 -1
- package/src/nowiki/dd.js +49 -0
- package/src/nowiki/hr.js +3 -2
- package/src/nowiki/list.js +16 -0
- package/src/parameter.js +3 -3
- package/src/table/index.js +35 -30
- package/src/table/td.js +2 -2
- package/src/table/tr.js +5 -11
- package/src/tagPair/index.js +1 -1
- package/src/transclude.js +14 -11
- package/tool/index.js +50 -40
- package/typings/index.d.ts +1 -0
- package/typings/node.d.ts +2 -2
- package/util/debug.js +3 -3
- package/util/string.js +4 -1
- package/src/listToken.js +0 -47
package/parser/table.js
CHANGED
|
@@ -54,10 +54,10 @@ const parseTable = ({firstChild, type}, config = Parser.getConfig(), accum = [])
|
|
|
54
54
|
}
|
|
55
55
|
const [, closing, row, cell, attr] = matches;
|
|
56
56
|
if (closing) {
|
|
57
|
-
while (top
|
|
57
|
+
while (!(top instanceof TableToken)) {
|
|
58
58
|
top = stack.pop();
|
|
59
59
|
}
|
|
60
|
-
top.close(`\n${spaces}${closing}
|
|
60
|
+
top.close(`\n${spaces}${closing}`, true);
|
|
61
61
|
push(attr, stack.at(-1));
|
|
62
62
|
} else if (row) {
|
|
63
63
|
if (top.type === 'td') {
|
package/printed/README
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
这里存放以 JSON 格式打印的 AST。
|
package/src/arg.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {text} = require('../util/string'),
|
|
3
|
+
const {text, noWrap} = require('../util/string'),
|
|
4
4
|
/** @type {Parser} */ Parser = require('..'),
|
|
5
5
|
Token = require('.');
|
|
6
6
|
|
|
@@ -118,7 +118,7 @@ class ArgToken extends Token {
|
|
|
118
118
|
const root = Parser.parse(`{{{${name}}}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
119
119
|
{childNodes: {length}, firstElementChild} = root;
|
|
120
120
|
if (length !== 1 || firstElementChild?.type !== 'arg' || firstElementChild.childElementCount !== 1) {
|
|
121
|
-
throw new SyntaxError(`非法的参数名称:${name
|
|
121
|
+
throw new SyntaxError(`非法的参数名称:${noWrap(name)}`);
|
|
122
122
|
}
|
|
123
123
|
const newName = firstElementChild.firstElementChild;
|
|
124
124
|
root.destroy();
|
|
@@ -132,7 +132,7 @@ class ArgToken extends Token {
|
|
|
132
132
|
const root = Parser.parse(`{{{|${value}}}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
133
133
|
{childNodes: {length}, firstElementChild} = root;
|
|
134
134
|
if (length !== 1 || firstElementChild?.type !== 'arg' || firstElementChild.childElementCount !== 2) {
|
|
135
|
-
throw new SyntaxError(`非法的参数预设值:${value
|
|
135
|
+
throw new SyntaxError(`非法的参数预设值:${noWrap(value)}`);
|
|
136
136
|
}
|
|
137
137
|
const [, oldDefault] = this.children,
|
|
138
138
|
newDefault = firstElementChild.lastElementChild;
|
package/src/attribute.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const {externalUse} = require('../util/debug'),
|
|
4
4
|
{toCase, removeComment} = require('../util/string'),
|
|
5
5
|
/** @type {Parser} */ Parser = require('..'),
|
|
6
6
|
Token = require('.');
|
|
@@ -28,14 +28,13 @@ class AttributeToken extends Token {
|
|
|
28
28
|
) {
|
|
29
29
|
equal = '{{=}}';
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
return [...this.#attr].map(([k, v]) => {
|
|
32
32
|
if (v === true) {
|
|
33
33
|
return k;
|
|
34
34
|
}
|
|
35
35
|
const quote = v.includes('"') ? "'" : '"';
|
|
36
36
|
return `${k}${equal}${quote}${v}${quote}`;
|
|
37
37
|
}).join(' ');
|
|
38
|
-
return str && ` ${str}`;
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
/** @complexity `n` */
|
|
@@ -56,17 +55,17 @@ class AttributeToken extends Token {
|
|
|
56
55
|
*/
|
|
57
56
|
#parseAttr() {
|
|
58
57
|
this.#attr.clear();
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
let string = this.toString(),
|
|
59
|
+
/** @type {Token & {firstChild: string}} */ token;
|
|
60
|
+
if (this.type !== 'ext-attr' && !Parser.running) {
|
|
61
|
+
const config = this.getAttribute('config'),
|
|
62
|
+
include = this.getAttribute('include');
|
|
63
|
+
token = Parser.run(() => new Token(string, config).parseOnce(0, include).parseOnce());
|
|
64
|
+
string = token.firstChild;
|
|
65
|
+
}
|
|
66
|
+
string = removeComment(string).replace(/\x00\d+~\x7f/g, '=');
|
|
67
|
+
const build = /** @param {string|boolean} str */ str =>
|
|
68
|
+
typeof str === 'boolean' || !token ? str : token.buildFromStr(str).map(String).join('');
|
|
70
69
|
for (const [, key,, quoted, unquoted] of string
|
|
71
70
|
.matchAll(/([^\s/][^\s/=]*)(?:\s*=\s*(?:(["'])(.*?)(?:\2|$)|(\S*)))?/sg)
|
|
72
71
|
) {
|
|
@@ -147,7 +146,7 @@ class AttributeToken extends Token {
|
|
|
147
146
|
/** @param {string} key */
|
|
148
147
|
hasAttr(key) {
|
|
149
148
|
if (typeof key !== 'string') {
|
|
150
|
-
typeError(
|
|
149
|
+
this.typeError('hasAttr', 'String');
|
|
151
150
|
}
|
|
152
151
|
return this.#attr.has(key.toLowerCase().trim());
|
|
153
152
|
}
|
|
@@ -161,7 +160,7 @@ class AttributeToken extends Token {
|
|
|
161
160
|
if (key === undefined) {
|
|
162
161
|
return Object.fromEntries(this.#attr);
|
|
163
162
|
} else if (typeof key !== 'string') {
|
|
164
|
-
typeError(
|
|
163
|
+
this.typeError('getAttr', 'String');
|
|
165
164
|
}
|
|
166
165
|
return this.#attr.get(key.toLowerCase().trim());
|
|
167
166
|
}
|
|
@@ -182,7 +181,7 @@ class AttributeToken extends Token {
|
|
|
182
181
|
setAttr(key, value, init = false) {
|
|
183
182
|
init &&= !externalUse('setAttr');
|
|
184
183
|
if (typeof key !== 'string' || !['string', 'boolean'].includes(typeof value)) {
|
|
185
|
-
typeError(
|
|
184
|
+
this.typeError('setValue', 'String', 'Boolean');
|
|
186
185
|
} else if (!init && this.type === 'ext-attr' && typeof value === 'string' && value.includes('>')) {
|
|
187
186
|
throw new RangeError('扩展标签属性不能包含 ">"!');
|
|
188
187
|
}
|
|
@@ -214,7 +213,7 @@ class AttributeToken extends Token {
|
|
|
214
213
|
*/
|
|
215
214
|
removeAttr(key) {
|
|
216
215
|
if (typeof key !== 'string') {
|
|
217
|
-
typeError(
|
|
216
|
+
this.typeError('removeAttr', 'String');
|
|
218
217
|
}
|
|
219
218
|
key = key.toLowerCase().trim();
|
|
220
219
|
if (this.#attr.delete(key)) {
|
|
@@ -229,7 +228,7 @@ class AttributeToken extends Token {
|
|
|
229
228
|
*/
|
|
230
229
|
toggleAttr(key, force) {
|
|
231
230
|
if (typeof key !== 'string') {
|
|
232
|
-
typeError(
|
|
231
|
+
this.typeError('toggleAttr', 'String');
|
|
233
232
|
} else if (force !== undefined) {
|
|
234
233
|
force = Boolean(force);
|
|
235
234
|
}
|
|
@@ -241,13 +240,24 @@ class AttributeToken extends Token {
|
|
|
241
240
|
this.setAttr(key, force === true || force === undefined && value === false);
|
|
242
241
|
}
|
|
243
242
|
|
|
243
|
+
#leadingSpace(str = super.toString()) {
|
|
244
|
+
return str && !/^\s/.test(str) ? ' ' : '';
|
|
245
|
+
}
|
|
246
|
+
|
|
244
247
|
toString() {
|
|
245
|
-
|
|
248
|
+
let str = super.toString();
|
|
249
|
+
str = `${this.#leadingSpace(str)}${str}`;
|
|
246
250
|
return this.type === 'table-attr' ? str.replaceAll('\n', ' ') : str;
|
|
247
251
|
}
|
|
248
252
|
|
|
253
|
+
getPadding() {
|
|
254
|
+
return this.#leadingSpace().length;
|
|
255
|
+
}
|
|
256
|
+
|
|
249
257
|
text() {
|
|
250
|
-
|
|
258
|
+
let str = this.#updateFromAttr();
|
|
259
|
+
str = `${this.#leadingSpace(str)}${str}`;
|
|
260
|
+
return this.type === 'table-attr' ? str.replaceAll('\n', ' ') : str;
|
|
251
261
|
}
|
|
252
262
|
|
|
253
263
|
/** @returns {[number, string][]} */
|
package/src/extLink.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const {noWrap} = require('../util/string'),
|
|
4
|
+
/** @type {Parser} */ Parser = require('..'),
|
|
4
5
|
Token = require('.'),
|
|
5
6
|
MagicLinkToken = require('./magicLink');
|
|
6
7
|
|
|
@@ -12,16 +13,13 @@ class ExtLinkToken extends Token {
|
|
|
12
13
|
type = 'ext-link';
|
|
13
14
|
#space;
|
|
14
15
|
|
|
15
|
-
/** @this {
|
|
16
|
+
/** @this {{firstChild: MagicLinkToken}} */
|
|
16
17
|
get protocol() {
|
|
17
|
-
return this.
|
|
18
|
+
return this.firstChild.protocol;
|
|
18
19
|
}
|
|
19
|
-
/**
|
|
20
|
-
* @this {ExtLinkToken & {firstElementChild: MagicLinkToken}}
|
|
21
|
-
* @param {string} value
|
|
22
|
-
*/
|
|
20
|
+
/** @this {{firstChild: MagicLinkToken}} */
|
|
23
21
|
set protocol(value) {
|
|
24
|
-
this.
|
|
22
|
+
this.firstChild.protocol = value;
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
/**
|
|
@@ -51,15 +49,27 @@ class ExtLinkToken extends Token {
|
|
|
51
49
|
}
|
|
52
50
|
}
|
|
53
51
|
|
|
52
|
+
#correct() {
|
|
53
|
+
if (!this.#space
|
|
54
|
+
// 都替换成`<`肯定不对,但无妨
|
|
55
|
+
&& /^[^[\]<>"\x00-\x20\x7f\p{Zs}\ufffd]/u.test(this.children[1]?.text().replace(/&[lg]t;/, '<'))
|
|
56
|
+
) {
|
|
57
|
+
this.#space = ' ';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
54
61
|
toString() {
|
|
62
|
+
this.#correct();
|
|
55
63
|
return `[${this.firstElementChild.toString()}${this.#space}${this.children[1]?.toString() ?? ''}]`;
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
getPadding() {
|
|
67
|
+
this.#correct();
|
|
59
68
|
return 1;
|
|
60
69
|
}
|
|
61
70
|
|
|
62
71
|
getGaps() {
|
|
72
|
+
this.#correct();
|
|
63
73
|
return this.#space.length;
|
|
64
74
|
}
|
|
65
75
|
|
|
@@ -100,7 +110,7 @@ class ExtLinkToken extends Token {
|
|
|
100
110
|
const root = Parser.parse(`[//url ${text}]`, this.getAttribute('include'), 8, this.getAttribute('config')),
|
|
101
111
|
{childNodes: {length}, firstElementChild} = root;
|
|
102
112
|
if (length !== 1 || firstElementChild?.type !== 'ext-link' || firstElementChild.childElementCount !== 2) {
|
|
103
|
-
throw new SyntaxError(`非法的外链文字:${text
|
|
113
|
+
throw new SyntaxError(`非法的外链文字:${noWrap(text)}`);
|
|
104
114
|
}
|
|
105
115
|
const {lastChild} = firstElementChild;
|
|
106
116
|
if (this.childElementCount === 1) {
|
package/src/heading.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const fixedToken = require('../mixin/fixedToken'),
|
|
4
|
+
sol = require('../mixin/sol'),
|
|
5
5
|
/** @type {Parser} */ Parser = require('..'),
|
|
6
6
|
Token = require('.');
|
|
7
7
|
|
|
@@ -9,7 +9,7 @@ const {typeError} = require('../util/debug'),
|
|
|
9
9
|
* 章节标题
|
|
10
10
|
* @classdesc `{childNodes: [Token, HiddenToken]}`
|
|
11
11
|
*/
|
|
12
|
-
class HeadingToken extends fixedToken(Token) {
|
|
12
|
+
class HeadingToken extends fixedToken(sol(Token)) {
|
|
13
13
|
type = 'heading';
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -38,34 +38,29 @@ class HeadingToken extends fixedToken(Token) {
|
|
|
38
38
|
return token;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/** @this {HeadingToken & {prependNewLine(): ''|'\n', appendNewLine(): ''|'\n'}} */
|
|
41
42
|
toString() {
|
|
42
|
-
const equals = '='.repeat(Number(this.name))
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|| previousVisibleSibling instanceof Token
|
|
47
|
-
? '\n'
|
|
48
|
-
: ''
|
|
49
|
-
}${equals}${super.toString(equals)}${
|
|
50
|
-
typeof nextVisibleSibling === 'string' && !nextVisibleSibling.startsWith('\n')
|
|
51
|
-
|| nextVisibleSibling instanceof Token
|
|
52
|
-
? '\n'
|
|
53
|
-
: ''
|
|
54
|
-
}`;
|
|
43
|
+
const equals = '='.repeat(Number(this.name));
|
|
44
|
+
return `${this.prependNewLine()}${equals}${
|
|
45
|
+
this.firstElementChild.toString()
|
|
46
|
+
}${equals}${this.lastElementChild.toString()}${this.appendNewLine()}`;
|
|
55
47
|
}
|
|
56
48
|
|
|
57
49
|
getPadding() {
|
|
58
|
-
return Number(this.name);
|
|
50
|
+
return super.getPadding() + Number(this.name);
|
|
59
51
|
}
|
|
60
52
|
|
|
61
53
|
getGaps() {
|
|
62
54
|
return Number(this.name);
|
|
63
55
|
}
|
|
64
56
|
|
|
65
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* @this {HeadingToken & {prependNewLine(): ''|'\n', appendNewLine(): ''|'\n'}}
|
|
59
|
+
* @returns {string}
|
|
60
|
+
*/
|
|
66
61
|
text() {
|
|
67
62
|
const equals = '='.repeat(Number(this.name));
|
|
68
|
-
return `${equals}${this.firstElementChild.text()}${equals}`;
|
|
63
|
+
return `${this.prependNewLine()}${equals}${this.firstElementChild.text()}${equals}${this.appendNewLine()}`;
|
|
69
64
|
}
|
|
70
65
|
|
|
71
66
|
/** @returns {[number, string][]} */
|
|
@@ -76,7 +71,7 @@ class HeadingToken extends fixedToken(Token) {
|
|
|
76
71
|
/** @param {number} n */
|
|
77
72
|
setLevel(n) {
|
|
78
73
|
if (typeof n !== 'number') {
|
|
79
|
-
typeError(
|
|
74
|
+
this.typeError('setLevel', 'Number');
|
|
80
75
|
}
|
|
81
76
|
n = Math.min(Math.max(n, 1), 6);
|
|
82
77
|
this.setAttribute('name', String(n)).firstElementChild.setAttribute('name', this.name);
|
package/src/html.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const {noWrap} = require('../util/string'),
|
|
4
|
+
fixedToken = require('../mixin/fixedToken'),
|
|
4
5
|
attributeParent = require('../mixin/attributeParent'),
|
|
5
6
|
/** @type {Parser} */ Parser = require('..'),
|
|
6
7
|
Token = require('.');
|
|
@@ -79,7 +80,7 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
|
|
|
79
80
|
findMatchingTag() {
|
|
80
81
|
const {html} = this.getAttribute('config'),
|
|
81
82
|
{name, parentElement, closing, selfClosing} = this,
|
|
82
|
-
string = this.toString()
|
|
83
|
+
string = noWrap(this.toString());
|
|
83
84
|
if (closing && selfClosing) {
|
|
84
85
|
throw new SyntaxError(`同时闭合和自封闭的标签:${string}`);
|
|
85
86
|
} else if (html[2].includes(name) || selfClosing && html[1].includes(name)) { // 自封闭标签
|
|
@@ -134,7 +135,7 @@ class HtmlToken extends attributeParent(fixedToken(Token)) {
|
|
|
134
135
|
this.selfClosing = false;
|
|
135
136
|
this.closing = true;
|
|
136
137
|
} else {
|
|
137
|
-
Parser.warn('无法修复无效自封闭标签', this.toString()
|
|
138
|
+
Parser.warn('无法修复无效自封闭标签', noWrap(this.toString()));
|
|
138
139
|
throw new Error(`无法修复无效自封闭标签:前文共有 ${imbalance} 个未匹配的闭合标签`);
|
|
139
140
|
}
|
|
140
141
|
}
|
package/src/imageParameter.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
{extUrlChar} = require('../util/string'),
|
|
3
|
+
const {text, noWrap, extUrlChar} = require('../util/string'),
|
|
5
4
|
Title = require('../lib/title'),
|
|
6
5
|
/** @type {Parser} */ Parser = require('..'),
|
|
7
6
|
Token = require('.');
|
|
@@ -14,9 +13,13 @@ class ImageParameterToken extends Token {
|
|
|
14
13
|
type = 'image-parameter';
|
|
15
14
|
#syntax = '';
|
|
16
15
|
|
|
16
|
+
static #noLink = Symbol('no-link');
|
|
17
|
+
|
|
17
18
|
/**
|
|
18
|
-
* @
|
|
19
|
+
* @template {string} T
|
|
20
|
+
* @param {T} key
|
|
19
21
|
* @param {string} value
|
|
22
|
+
* @returns {T extends 'link' ? string|Symbol : boolean}
|
|
20
23
|
*/
|
|
21
24
|
static #validate(key, value, config = Parser.getConfig()) {
|
|
22
25
|
value = value.replace(/\x00\d+t\x7f/g, '').trim();
|
|
@@ -26,11 +29,11 @@ class ImageParameterToken extends Token {
|
|
|
26
29
|
return true;
|
|
27
30
|
} else if (key === 'link') {
|
|
28
31
|
if (!value) {
|
|
29
|
-
return
|
|
32
|
+
return this.#noLink;
|
|
30
33
|
}
|
|
31
|
-
const regex = new RegExp(`(?:${config.protocol}|//)${extUrlChar}`, 'ui');
|
|
34
|
+
const regex = new RegExp(`(?:${config.protocol}|//)${extUrlChar}(?=\x00\\d+t\x7f|$)`, 'ui');
|
|
32
35
|
if (regex.test(value)) {
|
|
33
|
-
return
|
|
36
|
+
return value;
|
|
34
37
|
}
|
|
35
38
|
if (/^\[\[.+]]$/.test(value)) {
|
|
36
39
|
value = value.slice(2, -2);
|
|
@@ -40,11 +43,62 @@ class ImageParameterToken extends Token {
|
|
|
40
43
|
value = decodeURIComponent(value);
|
|
41
44
|
} catch {}
|
|
42
45
|
}
|
|
43
|
-
|
|
46
|
+
const {title, fragment, valid} = new Title(value, 0, config);
|
|
47
|
+
return valid && `${title}${fragment && '#'}${fragment}`;
|
|
44
48
|
}
|
|
45
49
|
return !isNaN(value);
|
|
46
50
|
}
|
|
47
51
|
|
|
52
|
+
get link() {
|
|
53
|
+
if (this.name === 'link') {
|
|
54
|
+
return ImageParameterToken.#validate('link', this.getValue(), this.getAttribute('config'));
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
set link(value) {
|
|
59
|
+
if (this.name === 'link') {
|
|
60
|
+
value = value === ImageParameterToken.#noLink ? '' : value;
|
|
61
|
+
this.setValue(value);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
get size() {
|
|
65
|
+
if (this.name === 'width') {
|
|
66
|
+
const /** @type {string} */ size = this.getValue().trim();
|
|
67
|
+
if (!size.includes('{{')) {
|
|
68
|
+
const [width, height = ''] = size.split('x');
|
|
69
|
+
return {width, height};
|
|
70
|
+
}
|
|
71
|
+
const token = Parser.parse(size, false, 2, this.getAttribute('config')),
|
|
72
|
+
{childNodes} = token,
|
|
73
|
+
i = childNodes.findIndex(child => typeof child === 'string' && child.includes('x'));
|
|
74
|
+
if (i === -1) {
|
|
75
|
+
return {width: size, height: ''};
|
|
76
|
+
}
|
|
77
|
+
token.splitText(i, childNodes[i].indexOf('x'));
|
|
78
|
+
token.splitText(i + 1, 1);
|
|
79
|
+
return {width: text(token.childNodes.slice(0, i + 1)), height: text(token.childNodes.slice(i + 2))};
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
get width() {
|
|
84
|
+
return this.size?.width;
|
|
85
|
+
}
|
|
86
|
+
set width(width) {
|
|
87
|
+
if (this.name === 'width') {
|
|
88
|
+
const {height} = this;
|
|
89
|
+
this.setValue(`${String(width || '')}${height && 'x'}${height}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
get height() {
|
|
93
|
+
return this.size?.height;
|
|
94
|
+
}
|
|
95
|
+
set height(height) {
|
|
96
|
+
height = String(height || '');
|
|
97
|
+
if (this.name === 'width') {
|
|
98
|
+
this.setValue(`${this.width}${height && 'x'}${height}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
48
102
|
/**
|
|
49
103
|
* @param {string} str
|
|
50
104
|
* @param {accum} accum
|
|
@@ -145,13 +199,13 @@ class ImageParameterToken extends Token {
|
|
|
145
199
|
setValue(value) {
|
|
146
200
|
if (this.#isVoid()) {
|
|
147
201
|
if (typeof value !== 'boolean') {
|
|
148
|
-
typeError(
|
|
202
|
+
this.typeError('setValue', 'Boolean');
|
|
149
203
|
} else if (value === false) {
|
|
150
204
|
this.remove();
|
|
151
205
|
}
|
|
152
206
|
return;
|
|
153
207
|
} else if (typeof value !== 'string') {
|
|
154
|
-
typeError(
|
|
208
|
+
this.typeError('setValue', 'String');
|
|
155
209
|
}
|
|
156
210
|
const root = Parser.parse(`[[File:F|${
|
|
157
211
|
this.#syntax ? this.#syntax.replace('$1', value) : value
|
|
@@ -161,7 +215,7 @@ class ImageParameterToken extends Token {
|
|
|
161
215
|
if (length !== 1 || !firstElementChild?.matches('file#File:F')
|
|
162
216
|
|| firstElementChild.childElementCount !== 2 || param.name !== this.name
|
|
163
217
|
) {
|
|
164
|
-
throw new SyntaxError(`非法的 ${this.name} 参数:${value
|
|
218
|
+
throw new SyntaxError(`非法的 ${this.name} 参数:${noWrap(value)}`);
|
|
165
219
|
}
|
|
166
220
|
this.replaceChildren(...param.childNodes);
|
|
167
221
|
}
|
package/src/index.js
CHANGED
|
@@ -37,9 +37,10 @@
|
|
|
37
37
|
* l: LinkToken
|
|
38
38
|
* q: QuoteToken
|
|
39
39
|
* w: ExtLinkToken
|
|
40
|
+
* d: ListToken
|
|
40
41
|
*/
|
|
41
42
|
|
|
42
|
-
const {
|
|
43
|
+
const {externalUse} = require('../util/debug'),
|
|
43
44
|
Ranges = require('../lib/ranges'),
|
|
44
45
|
AstElement = require('../lib/element'),
|
|
45
46
|
assert = require('assert/strict'),
|
|
@@ -255,7 +256,7 @@ class Token extends AstElement {
|
|
|
255
256
|
if (!parentNode) {
|
|
256
257
|
throw new Error('不存在父节点!');
|
|
257
258
|
} else if (token.constructor !== this.constructor) {
|
|
258
|
-
typeError(
|
|
259
|
+
this.typeError('safeReplaceWith', this.constructor.name);
|
|
259
260
|
}
|
|
260
261
|
try {
|
|
261
262
|
assert.deepEqual(token.getAttribute('acceptable'), this.#acceptable);
|
|
@@ -320,7 +321,7 @@ class Token extends AstElement {
|
|
|
320
321
|
*/
|
|
321
322
|
section(n) {
|
|
322
323
|
if (typeof n !== 'number') {
|
|
323
|
-
typeError(
|
|
324
|
+
this.typeError('section', 'Number');
|
|
324
325
|
}
|
|
325
326
|
return this.sections()[n];
|
|
326
327
|
}
|
|
@@ -332,7 +333,7 @@ class Token extends AstElement {
|
|
|
332
333
|
*/
|
|
333
334
|
findEnclosingHtml(tag) {
|
|
334
335
|
if (tag !== undefined && typeof tag !== 'string') {
|
|
335
|
-
typeError(
|
|
336
|
+
this.typeError('findEnclosingHtml', 'String');
|
|
336
337
|
}
|
|
337
338
|
tag = tag?.toLowerCase();
|
|
338
339
|
if (tag !== undefined && !this.#config.html.slice(0, 2).flat().includes(tag)) {
|
|
@@ -436,11 +437,7 @@ class Token extends AstElement {
|
|
|
436
437
|
this.#parseLinks();
|
|
437
438
|
break;
|
|
438
439
|
case 6: {
|
|
439
|
-
|
|
440
|
-
for (let i = 0; i < lines.length; i++) {
|
|
441
|
-
lines[i] = this.#parseQuotes(lines[i]);
|
|
442
|
-
}
|
|
443
|
-
this.setText(lines.join('\n'));
|
|
440
|
+
this.#parseQuotes();
|
|
444
441
|
break;
|
|
445
442
|
}
|
|
446
443
|
case 7:
|
|
@@ -450,6 +447,7 @@ class Token extends AstElement {
|
|
|
450
447
|
this.#parseMagicLinks();
|
|
451
448
|
break;
|
|
452
449
|
case 9:
|
|
450
|
+
this.#parseList();
|
|
453
451
|
break;
|
|
454
452
|
case 10:
|
|
455
453
|
// no default
|
|
@@ -519,7 +517,7 @@ class Token extends AstElement {
|
|
|
519
517
|
/** 解析、重构、生成部分Token的`name`属性 */
|
|
520
518
|
parse(n = MAX_STAGE, include = false) {
|
|
521
519
|
if (typeof n !== 'number') {
|
|
522
|
-
typeError(
|
|
520
|
+
this.typeError('parse', 'Number');
|
|
523
521
|
} else if (n < MAX_STAGE && !Parser.debugging && externalUse('parse')) {
|
|
524
522
|
Parser.warn('指定解析层级的方法仅供熟练用户使用!');
|
|
525
523
|
}
|
|
@@ -579,10 +577,14 @@ class Token extends AstElement {
|
|
|
579
577
|
this.setText(parseLinks(this.firstChild, this.#config, this.#accum));
|
|
580
578
|
}
|
|
581
579
|
|
|
582
|
-
/** @
|
|
583
|
-
#parseQuotes(
|
|
584
|
-
const parseQuotes = require('../parser/quotes')
|
|
585
|
-
|
|
580
|
+
/** @this {Token & {firstChild: string}} */
|
|
581
|
+
#parseQuotes() {
|
|
582
|
+
const parseQuotes = require('../parser/quotes'),
|
|
583
|
+
lines = this.firstChild.split('\n');
|
|
584
|
+
for (let i = 0; i < lines.length; i++) {
|
|
585
|
+
lines[i] = parseQuotes(lines[i], this.#config, this.#accum);
|
|
586
|
+
}
|
|
587
|
+
this.setText(lines.join('\n'));
|
|
586
588
|
}
|
|
587
589
|
|
|
588
590
|
/** @this {Token & {firstChild: string}} */
|
|
@@ -596,6 +598,15 @@ class Token extends AstElement {
|
|
|
596
598
|
const parseMagicLinks = require('../parser/magicLinks');
|
|
597
599
|
this.setText(parseMagicLinks(this.firstChild, this.#config, this.#accum));
|
|
598
600
|
}
|
|
601
|
+
|
|
602
|
+
#parseList() {
|
|
603
|
+
const parseList = require('../parser/list'),
|
|
604
|
+
lines = this.firstChild.split('\n');
|
|
605
|
+
for (let i = 0; i < lines.length; i++) {
|
|
606
|
+
lines[i] = parseList(lines[i], this.#config, this.#accum);
|
|
607
|
+
}
|
|
608
|
+
this.setText(lines.join('\n'));
|
|
609
|
+
}
|
|
599
610
|
}
|
|
600
611
|
|
|
601
612
|
Parser.classes.Token = __filename;
|
package/src/link/file.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {explode} = require('../../util/string'),
|
|
4
|
-
{
|
|
3
|
+
const {explode, noWrap} = require('../../util/string'),
|
|
4
|
+
{externalUse} = require('../../util/debug'),
|
|
5
5
|
/** @type {Parser} */ Parser = require('../..'),
|
|
6
6
|
LinkToken = require('.'),
|
|
7
7
|
ImageParameterToken = require('../imageParameter');
|
|
@@ -20,6 +20,38 @@ class FileToken extends LinkToken {
|
|
|
20
20
|
setLinkText = undefined;
|
|
21
21
|
pipeTrick = undefined;
|
|
22
22
|
|
|
23
|
+
get link() {
|
|
24
|
+
return this.getArg('link')?.link;
|
|
25
|
+
}
|
|
26
|
+
set link(value) {
|
|
27
|
+
this.setValue('link', value);
|
|
28
|
+
}
|
|
29
|
+
get size() {
|
|
30
|
+
return this.getArg('width')?.size;
|
|
31
|
+
}
|
|
32
|
+
get width() {
|
|
33
|
+
return this.size?.width;
|
|
34
|
+
}
|
|
35
|
+
set width(width) {
|
|
36
|
+
const arg = this.getArg('width');
|
|
37
|
+
if (arg) {
|
|
38
|
+
arg.width = width;
|
|
39
|
+
} else {
|
|
40
|
+
this.setValue('width', width);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
get height() {
|
|
44
|
+
return this.size?.height;
|
|
45
|
+
}
|
|
46
|
+
set height(height) {
|
|
47
|
+
const arg = this.getArg('width');
|
|
48
|
+
if (arg) {
|
|
49
|
+
arg.height = height;
|
|
50
|
+
} else {
|
|
51
|
+
this.setValue('width', `x${height}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
23
55
|
/**
|
|
24
56
|
* @param {string} link
|
|
25
57
|
* @param {string|undefined} text
|
|
@@ -70,7 +102,7 @@ class FileToken extends LinkToken {
|
|
|
70
102
|
const args = this.getAllArgs()
|
|
71
103
|
.filter(({name}) => ['manualthumb', 'frameless', 'framed', 'thumbnail'].includes(name));
|
|
72
104
|
if (args.length > 1) {
|
|
73
|
-
Parser.error(
|
|
105
|
+
Parser.error(`图片 ${this.name} 带有 ${args.length} 个框架参数,只有第 1 个 ${args[0].name} 会生效!`);
|
|
74
106
|
}
|
|
75
107
|
return args;
|
|
76
108
|
}
|
|
@@ -81,7 +113,7 @@ class FileToken extends LinkToken {
|
|
|
81
113
|
*/
|
|
82
114
|
getArgs(key, copy = true) {
|
|
83
115
|
if (typeof key !== 'string') {
|
|
84
|
-
typeError(
|
|
116
|
+
this.typeError('getArgs', 'String');
|
|
85
117
|
} else if (!copy && !Parser.debugging && externalUse('getArgs')) {
|
|
86
118
|
this.debugOnly('getArgs');
|
|
87
119
|
}
|
|
@@ -158,7 +190,7 @@ class FileToken extends LinkToken {
|
|
|
158
190
|
*/
|
|
159
191
|
setValue(key, value) {
|
|
160
192
|
if (typeof key !== 'string') {
|
|
161
|
-
typeError(
|
|
193
|
+
this.typeError('setValue', 'String');
|
|
162
194
|
} else if (value === false) {
|
|
163
195
|
this.removeArg(key);
|
|
164
196
|
return;
|
|
@@ -179,7 +211,7 @@ class FileToken extends LinkToken {
|
|
|
179
211
|
}
|
|
180
212
|
if (value === true) {
|
|
181
213
|
if (syntax.includes('$1')) {
|
|
182
|
-
typeError(
|
|
214
|
+
this.typeError('setValue', 'Boolean');
|
|
183
215
|
}
|
|
184
216
|
const newArg = Parser.run(() => new ImageParameterToken(syntax, config));
|
|
185
217
|
this.appendChild(newArg);
|
|
@@ -191,7 +223,7 @@ class FileToken extends LinkToken {
|
|
|
191
223
|
if (length !== 1 || !firstElementChild?.matches('file#File:F')
|
|
192
224
|
|| firstElementChild.childElementCount !== 2 || firstElementChild.lastElementChild.name !== key
|
|
193
225
|
) {
|
|
194
|
-
throw new SyntaxError(`非法的 ${key} 参数:${value
|
|
226
|
+
throw new SyntaxError(`非法的 ${key} 参数:${noWrap(value)}`);
|
|
195
227
|
}
|
|
196
228
|
this.appendChild(firstElementChild.lastChild);
|
|
197
229
|
}
|
package/src/link/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const Title = require('../../lib/title'), // eslint-disable-line no-unused-vars
|
|
4
|
-
{text} = require('../../util/string'),
|
|
4
|
+
{text, noWrap} = require('../../util/string'),
|
|
5
5
|
{undo} = require('../../util/debug'),
|
|
6
6
|
/** @type {Parser} */ Parser = require('../..'),
|
|
7
7
|
Token = require('..');
|
|
@@ -170,9 +170,7 @@ class LinkToken extends Token {
|
|
|
170
170
|
}L|${linkText}]]`, this.getAttribute('include'), 6, config),
|
|
171
171
|
{childNodes: {length}, firstElementChild} = root;
|
|
172
172
|
if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.childElementCount !== 2) {
|
|
173
|
-
throw new SyntaxError(`非法的${this.type === 'link' ? '内链文字' : '分类关键字'}:${
|
|
174
|
-
linkText.replaceAll('\n', '\\n')
|
|
175
|
-
}`);
|
|
173
|
+
throw new SyntaxError(`非法的${this.type === 'link' ? '内链文字' : '分类关键字'}:${noWrap(linkText)}`);
|
|
176
174
|
}
|
|
177
175
|
({lastElementChild} = firstElementChild);
|
|
178
176
|
} else {
|