wikiparser-node 0.0.2 → 0.2.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 +226 -7
- package/config/default.json +12 -1
- package/config/llwiki.json +12 -1
- package/config/moegirl.json +9 -1
- package/errors/2022-07-04T22:30:41.785Z +1 -0
- package/errors/2022-07-04T22:30:41.785Z.err +11 -0
- package/errors/2022-07-04T22:30:41.785Z.json +5 -0
- package/errors/README +1 -0
- package/index.js +85 -9
- package/lib/element.js +72 -13
- package/lib/node.js +17 -9
- package/mixin/sol.js +42 -0
- package/package.json +1 -1
- package/parser/converter.js +44 -0
- 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 +9 -9
- package/src/attribute.js +33 -23
- package/src/converter.js +135 -0
- package/src/converterFlags.js +214 -0
- package/src/converterRule.js +209 -0
- package/src/extLink.js +23 -10
- package/src/heading.js +15 -20
- package/src/html.js +4 -3
- package/src/imageParameter.js +6 -7
- package/src/index.js +38 -23
- package/src/link/file.js +9 -9
- package/src/link/index.js +9 -11
- package/src/magicLink.js +2 -3
- 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 +5 -5
- package/src/syntax.js +3 -1
- package/src/table/index.js +35 -30
- package/src/table/td.js +3 -2
- package/src/table/tr.js +6 -12
- package/src/tagPair/index.js +1 -1
- package/src/transclude.js +28 -25
- package/tool/index.js +50 -40
- package/typings/index.d.ts +3 -0
- package/typings/node.d.ts +3 -3
- package/typings/token.d.ts +1 -0
- package/util/debug.js +3 -3
- package/util/string.js +16 -1
- package/src/listToken.js +0 -47
package/src/index.js
CHANGED
|
@@ -37,9 +37,11 @@
|
|
|
37
37
|
* l: LinkToken
|
|
38
38
|
* q: QuoteToken
|
|
39
39
|
* w: ExtLinkToken
|
|
40
|
+
* d: ListToken
|
|
41
|
+
* v: ConverterToken
|
|
40
42
|
*/
|
|
41
43
|
|
|
42
|
-
const {
|
|
44
|
+
const {externalUse} = require('../util/debug'),
|
|
43
45
|
Ranges = require('../lib/ranges'),
|
|
44
46
|
AstElement = require('../lib/element'),
|
|
45
47
|
assert = require('assert/strict'),
|
|
@@ -201,9 +203,13 @@ class Token extends AstElement {
|
|
|
201
203
|
* @complexity `n`
|
|
202
204
|
*/
|
|
203
205
|
removeAt(i) {
|
|
206
|
+
if (typeof i !== 'number') {
|
|
207
|
+
this.typeError('removeAt', 'Number');
|
|
208
|
+
}
|
|
209
|
+
const iPos = i < 0 ? i + this.childNodes.length : i;
|
|
204
210
|
if (!Parser.running) {
|
|
205
211
|
const protectedIndices = this.#protectedChildren.applyTo(this.childNodes);
|
|
206
|
-
if (protectedIndices.includes(
|
|
212
|
+
if (protectedIndices.includes(iPos)) {
|
|
207
213
|
throw new Error(`${this.constructor.name} 的第 ${i} 个子节点不可移除!`);
|
|
208
214
|
} else if (this.#acceptable) {
|
|
209
215
|
const acceptableIndices = Object.fromEntries(
|
|
@@ -255,7 +261,7 @@ class Token extends AstElement {
|
|
|
255
261
|
if (!parentNode) {
|
|
256
262
|
throw new Error('不存在父节点!');
|
|
257
263
|
} else if (token.constructor !== this.constructor) {
|
|
258
|
-
typeError(
|
|
264
|
+
this.typeError('safeReplaceWith', this.constructor.name);
|
|
259
265
|
}
|
|
260
266
|
try {
|
|
261
267
|
assert.deepEqual(token.getAttribute('acceptable'), this.#acceptable);
|
|
@@ -320,7 +326,7 @@ class Token extends AstElement {
|
|
|
320
326
|
*/
|
|
321
327
|
section(n) {
|
|
322
328
|
if (typeof n !== 'number') {
|
|
323
|
-
typeError(
|
|
329
|
+
this.typeError('section', 'Number');
|
|
324
330
|
}
|
|
325
331
|
return this.sections()[n];
|
|
326
332
|
}
|
|
@@ -332,7 +338,7 @@ class Token extends AstElement {
|
|
|
332
338
|
*/
|
|
333
339
|
findEnclosingHtml(tag) {
|
|
334
340
|
if (tag !== undefined && typeof tag !== 'string') {
|
|
335
|
-
typeError(
|
|
341
|
+
this.typeError('findEnclosingHtml', 'String');
|
|
336
342
|
}
|
|
337
343
|
tag = tag?.toLowerCase();
|
|
338
344
|
if (tag !== undefined && !this.#config.html.slice(0, 2).flat().includes(tag)) {
|
|
@@ -436,11 +442,7 @@ class Token extends AstElement {
|
|
|
436
442
|
this.#parseLinks();
|
|
437
443
|
break;
|
|
438
444
|
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'));
|
|
445
|
+
this.#parseQuotes();
|
|
444
446
|
break;
|
|
445
447
|
}
|
|
446
448
|
case 7:
|
|
@@ -450,8 +452,10 @@ class Token extends AstElement {
|
|
|
450
452
|
this.#parseMagicLinks();
|
|
451
453
|
break;
|
|
452
454
|
case 9:
|
|
455
|
+
this.#parseList();
|
|
453
456
|
break;
|
|
454
457
|
case 10:
|
|
458
|
+
this.#parseConverter();
|
|
455
459
|
// no default
|
|
456
460
|
}
|
|
457
461
|
if (this.type === 'root') {
|
|
@@ -519,7 +523,7 @@ class Token extends AstElement {
|
|
|
519
523
|
/** 解析、重构、生成部分Token的`name`属性 */
|
|
520
524
|
parse(n = MAX_STAGE, include = false) {
|
|
521
525
|
if (typeof n !== 'number') {
|
|
522
|
-
typeError(
|
|
526
|
+
this.typeError('parse', 'Number');
|
|
523
527
|
} else if (n < MAX_STAGE && !Parser.debugging && externalUse('parse')) {
|
|
524
528
|
Parser.warn('指定解析层级的方法仅供熟练用户使用!');
|
|
525
529
|
}
|
|
@@ -530,25 +534,21 @@ class Token extends AstElement {
|
|
|
530
534
|
return n ? this.build().afterBuild() : this;
|
|
531
535
|
}
|
|
532
536
|
|
|
533
|
-
/** @this {Token & {firstChild: string}} */
|
|
534
537
|
#parseCommentAndExt(includeOnly = false) {
|
|
535
538
|
const parseCommentAndExt = require('../parser/commentAndExt');
|
|
536
539
|
this.setText(parseCommentAndExt(this.firstChild, this.#config, this.#accum, includeOnly));
|
|
537
540
|
}
|
|
538
541
|
|
|
539
|
-
/** @this {Token & {firstChild: string}} */
|
|
540
542
|
#parseBrackets() {
|
|
541
543
|
const parseBrackets = require('../parser/brackets');
|
|
542
544
|
this.setText(parseBrackets(this.firstChild, this.#config, this.#accum));
|
|
543
545
|
}
|
|
544
546
|
|
|
545
|
-
/** @this {Token & {firstChild: string}} */
|
|
546
547
|
#parseHtml() {
|
|
547
548
|
const parseHtml = require('../parser/html');
|
|
548
549
|
this.setText(parseHtml(this.firstChild, this.#config, this.#accum));
|
|
549
550
|
}
|
|
550
551
|
|
|
551
|
-
/** @this {Token & {firstChild: string}} */
|
|
552
552
|
#parseTable() {
|
|
553
553
|
const parseTable = require('../parser/table'),
|
|
554
554
|
TableToken = require('./table');
|
|
@@ -567,35 +567,50 @@ class Token extends AstElement {
|
|
|
567
567
|
}
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
-
/** @this {Token & {firstChild: string}} */
|
|
571
570
|
#parseHrAndDoubleUndescore() {
|
|
572
571
|
const parseHrAndDoubleUnderscore = require('../parser/hrAndDoubleUnderscore');
|
|
573
572
|
this.setText(parseHrAndDoubleUnderscore(this.firstChild, this.#config, this.#accum));
|
|
574
573
|
}
|
|
575
574
|
|
|
576
|
-
/** @this {Token & {firstChild: string}} */
|
|
577
575
|
#parseLinks() {
|
|
578
576
|
const parseLinks = require('../parser/links');
|
|
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
|
-
/** @this {Token & {firstChild: string}} */
|
|
589
590
|
#parseExternalLinks() {
|
|
590
591
|
const parseExternalLinks = require('../parser/externalLinks');
|
|
591
592
|
this.setText(parseExternalLinks(this.firstChild, this.#config, this.#accum));
|
|
592
593
|
}
|
|
593
594
|
|
|
594
|
-
/** @this {Token & {firstChild: string}} */
|
|
595
595
|
#parseMagicLinks() {
|
|
596
596
|
const parseMagicLinks = require('../parser/magicLinks');
|
|
597
597
|
this.setText(parseMagicLinks(this.firstChild, this.#config, this.#accum));
|
|
598
598
|
}
|
|
599
|
+
|
|
600
|
+
/** @this {Token & {firstChild: string}} */
|
|
601
|
+
#parseList() {
|
|
602
|
+
const parseList = require('../parser/list'),
|
|
603
|
+
lines = this.firstChild.split('\n');
|
|
604
|
+
for (let i = this.type === 'root' ? 0 : 1; i < lines.length; i++) {
|
|
605
|
+
lines[i] = parseList(lines[i], this.#config, this.#accum);
|
|
606
|
+
}
|
|
607
|
+
this.setText(lines.join('\n'));
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
#parseConverter() {
|
|
611
|
+
const parseConverter = require('../parser/converter');
|
|
612
|
+
this.setText(parseConverter(this.firstChild, this.#config, this.#accum));
|
|
613
|
+
}
|
|
599
614
|
}
|
|
600
615
|
|
|
601
616
|
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');
|
|
@@ -84,7 +84,7 @@ class FileToken extends LinkToken {
|
|
|
84
84
|
* @param {ImageParameterToken} token
|
|
85
85
|
* @complexity `n`
|
|
86
86
|
*/
|
|
87
|
-
insertAt(token, i = this.
|
|
87
|
+
insertAt(token, i = this.childNodes.length) {
|
|
88
88
|
if (!Parser.running) {
|
|
89
89
|
this.getArgs(token.name, false, false).add(token);
|
|
90
90
|
this.#keys.add(token.name);
|
|
@@ -102,7 +102,7 @@ class FileToken extends LinkToken {
|
|
|
102
102
|
const args = this.getAllArgs()
|
|
103
103
|
.filter(({name}) => ['manualthumb', 'frameless', 'framed', 'thumbnail'].includes(name));
|
|
104
104
|
if (args.length > 1) {
|
|
105
|
-
Parser.error(
|
|
105
|
+
Parser.error(`图片 ${this.name} 带有 ${args.length} 个框架参数,只有第 1 个 ${args[0].name} 会生效!`);
|
|
106
106
|
}
|
|
107
107
|
return args;
|
|
108
108
|
}
|
|
@@ -113,7 +113,7 @@ class FileToken extends LinkToken {
|
|
|
113
113
|
*/
|
|
114
114
|
getArgs(key, copy = true) {
|
|
115
115
|
if (typeof key !== 'string') {
|
|
116
|
-
typeError(
|
|
116
|
+
this.typeError('getArgs', 'String');
|
|
117
117
|
} else if (!copy && !Parser.debugging && externalUse('getArgs')) {
|
|
118
118
|
this.debugOnly('getArgs');
|
|
119
119
|
}
|
|
@@ -190,7 +190,7 @@ class FileToken extends LinkToken {
|
|
|
190
190
|
*/
|
|
191
191
|
setValue(key, value) {
|
|
192
192
|
if (typeof key !== 'string') {
|
|
193
|
-
typeError(
|
|
193
|
+
this.typeError('setValue', 'String');
|
|
194
194
|
} else if (value === false) {
|
|
195
195
|
this.removeArg(key);
|
|
196
196
|
return;
|
|
@@ -211,7 +211,7 @@ class FileToken extends LinkToken {
|
|
|
211
211
|
}
|
|
212
212
|
if (value === true) {
|
|
213
213
|
if (syntax.includes('$1')) {
|
|
214
|
-
typeError(
|
|
214
|
+
this.typeError('setValue', 'Boolean');
|
|
215
215
|
}
|
|
216
216
|
const newArg = Parser.run(() => new ImageParameterToken(syntax, config));
|
|
217
217
|
this.appendChild(newArg);
|
|
@@ -221,9 +221,9 @@ class FileToken extends LinkToken {
|
|
|
221
221
|
root = Parser.parse(wikitext, this.getAttribute('include'), 6, config),
|
|
222
222
|
{childNodes: {length}, firstElementChild} = root;
|
|
223
223
|
if (length !== 1 || !firstElementChild?.matches('file#File:F')
|
|
224
|
-
|| firstElementChild.
|
|
224
|
+
|| firstElementChild.childNodes.length !== 2 || firstElementChild.lastElementChild.name !== key
|
|
225
225
|
) {
|
|
226
|
-
throw new SyntaxError(`非法的 ${key} 参数:${value
|
|
226
|
+
throw new SyntaxError(`非法的 ${key} 参数:${noWrap(value)}`);
|
|
227
227
|
}
|
|
228
228
|
this.appendChild(firstElementChild.lastChild);
|
|
229
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('..');
|
|
@@ -29,9 +29,9 @@ class LinkToken extends Token {
|
|
|
29
29
|
'Stage-2': ':', '!ExtToken': '', '!HeadingToken': '',
|
|
30
30
|
}));
|
|
31
31
|
if (linkText !== undefined) {
|
|
32
|
-
const inner = new Token(linkText, config, true, accum);
|
|
32
|
+
const inner = new Token(linkText, config, true, accum, {'Stage-5': ':', ConverterToken: ':'});
|
|
33
33
|
inner.type = 'link-text';
|
|
34
|
-
this.appendChild(inner.setAttribute('stage',
|
|
34
|
+
this.appendChild(inner.setAttribute('stage', Parser.MAX_STAGE - 1));
|
|
35
35
|
}
|
|
36
36
|
this.selfLink = !title.title;
|
|
37
37
|
this.fragment = title.fragment;
|
|
@@ -107,7 +107,7 @@ class LinkToken extends Token {
|
|
|
107
107
|
|
|
108
108
|
/** @returns {[number, string][]} */
|
|
109
109
|
plain() {
|
|
110
|
-
return this.
|
|
110
|
+
return this.childNodes.length === 1 ? [] : this.lastElementChild.plain();
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/** @param {string} link */
|
|
@@ -118,7 +118,7 @@ class LinkToken extends Token {
|
|
|
118
118
|
}
|
|
119
119
|
const root = Parser.parse(`[[${link}]]`, this.getAttribute('include'), 6, this.getAttribute('config')),
|
|
120
120
|
{childNodes: {length}, firstElementChild} = root;
|
|
121
|
-
if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.
|
|
121
|
+
if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.childNodes.length !== 1) {
|
|
122
122
|
const msgs = {link: '内链', file: '文件链接', category: '分类'};
|
|
123
123
|
throw new SyntaxError(`非法的${msgs[this.type]}目标:${link}`);
|
|
124
124
|
}
|
|
@@ -135,7 +135,7 @@ class LinkToken extends Token {
|
|
|
135
135
|
config = this.getAttribute('config'),
|
|
136
136
|
root = Parser.parse(`[[${page ? `:${this.name}` : ''}#${fragment}]]`, include, 6, config),
|
|
137
137
|
{childNodes: {length}, firstElementChild} = root;
|
|
138
|
-
if (length !== 1 || firstElementChild?.type !== 'link' || firstElementChild.
|
|
138
|
+
if (length !== 1 || firstElementChild?.type !== 'link' || firstElementChild.childNodes.length !== 1) {
|
|
139
139
|
throw new SyntaxError(`非法的 fragment:${fragment}`);
|
|
140
140
|
}
|
|
141
141
|
if (page) {
|
|
@@ -169,17 +169,15 @@ class LinkToken extends Token {
|
|
|
169
169
|
this.type === 'category' ? 'Category:' : ''
|
|
170
170
|
}L|${linkText}]]`, this.getAttribute('include'), 6, config),
|
|
171
171
|
{childNodes: {length}, firstElementChild} = root;
|
|
172
|
-
if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.
|
|
173
|
-
throw new SyntaxError(`非法的${this.type === 'link' ? '内链文字' : '分类关键字'}:${
|
|
174
|
-
linkText.replaceAll('\n', '\\n')
|
|
175
|
-
}`);
|
|
172
|
+
if (length !== 1 || firstElementChild?.type !== this.type || firstElementChild.childNodes.length !== 2) {
|
|
173
|
+
throw new SyntaxError(`非法的${this.type === 'link' ? '内链文字' : '分类关键字'}:${noWrap(linkText)}`);
|
|
176
174
|
}
|
|
177
175
|
({lastElementChild} = firstElementChild);
|
|
178
176
|
} else {
|
|
179
177
|
lastElementChild = Parser.run(() => new Token('', config));
|
|
180
178
|
lastElementChild.setAttribute('stage', 7).type = 'link-text';
|
|
181
179
|
}
|
|
182
|
-
if (this.
|
|
180
|
+
if (this.childNodes.length === 1) {
|
|
183
181
|
this.appendChild(lastElementChild);
|
|
184
182
|
} else {
|
|
185
183
|
this.lastElementChild.safeReplaceWith(lastElementChild);
|
package/src/magicLink.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
/** @type {Parser} */ Parser = require('..'),
|
|
3
|
+
const /** @type {Parser} */ Parser = require('..'),
|
|
5
4
|
Token = require('.');
|
|
6
5
|
|
|
7
6
|
/**
|
|
@@ -17,7 +16,7 @@ class MagicLinkToken extends Token {
|
|
|
17
16
|
}
|
|
18
17
|
set protocol(value) {
|
|
19
18
|
if (typeof value !== 'string') {
|
|
20
|
-
typeError(
|
|
19
|
+
this.typeError('protocol', 'String');
|
|
21
20
|
}
|
|
22
21
|
if (!new RegExp(`${this.#protocolRegex.source}$`, 'i').test(value)) {
|
|
23
22
|
throw new RangeError(`非法的外链协议:${value}`);
|
package/src/nowiki/comment.js
CHANGED
|
@@ -30,7 +30,7 @@ class CommentToken extends hidden(NowikiToken) {
|
|
|
30
30
|
toString() {
|
|
31
31
|
const {firstChild, closed, nextSibling} = this;
|
|
32
32
|
if (!closed && nextSibling) {
|
|
33
|
-
Parser.error('自动闭合HTML注释',
|
|
33
|
+
Parser.error('自动闭合HTML注释', this);
|
|
34
34
|
this.closed = true;
|
|
35
35
|
}
|
|
36
36
|
return `<!--${firstChild}${this.closed ? '-->' : ''}`;
|
package/src/nowiki/dd.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const /** @type {Parser} */ Parser = require('../..'),
|
|
4
|
+
NowikiToken = require('.');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* :
|
|
8
|
+
* @classdesc `{childNodes: [string]}`
|
|
9
|
+
*/
|
|
10
|
+
class DdToken extends NowikiToken {
|
|
11
|
+
type = 'dd';
|
|
12
|
+
dt;
|
|
13
|
+
ul;
|
|
14
|
+
ol;
|
|
15
|
+
indent;
|
|
16
|
+
|
|
17
|
+
/** @param {string} str */
|
|
18
|
+
#update(str) {
|
|
19
|
+
this.setAttribute('ul', str.includes('*')).setAttribute('ol', str.includes('#'))
|
|
20
|
+
.setAttribute('dt', str.includes(';')).setAttribute('indent', str.split(':').length - 1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {string} str
|
|
25
|
+
* @param {accum} accum
|
|
26
|
+
*/
|
|
27
|
+
constructor(str, config = Parser.getConfig(), accum = []) {
|
|
28
|
+
super(str, config, accum);
|
|
29
|
+
this.seal(['dt', 'ul', 'ol', 'indent']).#update(str);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** @returns {[number, string][]} */
|
|
33
|
+
plain() {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** @param {string} str */
|
|
38
|
+
setText(str) {
|
|
39
|
+
const src = this.type === 'dd' ? ':' : ';:*#';
|
|
40
|
+
if (new RegExp(`[^${src}]`).test(str)) {
|
|
41
|
+
throw new RangeError(`${this.constructor.name} 仅能包含${src.split('').map(c => `"${c}"`).join('、')}!`);
|
|
42
|
+
}
|
|
43
|
+
this.#update(str);
|
|
44
|
+
return super.setText(str);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Parser.classes.DdToken = __filename;
|
|
49
|
+
module.exports = DdToken;
|
package/src/nowiki/hr.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const sol = require('../../mixin/sol'),
|
|
4
|
+
/** @type {Parser} */ Parser = require('../..'),
|
|
4
5
|
NowikiToken = require('.');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* `<hr>`
|
|
8
9
|
* @classdesc `{childNodes: [string]}`
|
|
9
10
|
*/
|
|
10
|
-
class HrToken extends NowikiToken {
|
|
11
|
+
class HrToken extends sol(NowikiToken) {
|
|
11
12
|
type = 'hr';
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const sol = require('../../mixin/sol'),
|
|
4
|
+
/** @type {Parser} */ Parser = require('../..'),
|
|
5
|
+
DdToken = require('./dd');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ;:*#
|
|
9
|
+
* @classdesc `{childNodes: [string]}`
|
|
10
|
+
*/
|
|
11
|
+
class ListToken extends sol(DdToken) {
|
|
12
|
+
type = 'list';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
Parser.classes.ListToken = __filename;
|
|
16
|
+
module.exports = ListToken;
|
package/src/parameter.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const {noWrap} = require('../util/string'),
|
|
4
4
|
fixedToken = require('../mixin/fixedToken'),
|
|
5
5
|
/** @type {Parser} */ Parser = require('..'),
|
|
6
6
|
Token = require('.');
|
|
@@ -113,10 +113,10 @@ class ParameterToken extends fixedToken(Token) {
|
|
|
113
113
|
{childNodes: {length}, firstElementChild} = root,
|
|
114
114
|
/** @type {ParameterToken} */ lastElementChild = firstElementChild?.lastElementChild;
|
|
115
115
|
if (length !== 1 || !firstElementChild?.matches(templateLike ? 'template#T' : 'magic-word#lc')
|
|
116
|
-
|| firstElementChild.
|
|
116
|
+
|| firstElementChild.childNodes.length !== 2
|
|
117
117
|
|| lastElementChild.anon !== this.anon || lastElementChild.name !== '1'
|
|
118
118
|
) {
|
|
119
|
-
throw new SyntaxError(`非法的模板参数:${value
|
|
119
|
+
throw new SyntaxError(`非法的模板参数:${noWrap(value)}`);
|
|
120
120
|
}
|
|
121
121
|
const newValue = lastElementChild.lastChild;
|
|
122
122
|
root.destroy();
|
|
@@ -128,7 +128,7 @@ class ParameterToken extends fixedToken(Token) {
|
|
|
128
128
|
/** @param {string} key */
|
|
129
129
|
rename(key, force = false) {
|
|
130
130
|
if (typeof key !== 'string') {
|
|
131
|
-
typeError(
|
|
131
|
+
this.typeError('rename', 'String');
|
|
132
132
|
}
|
|
133
133
|
const {parentNode} = this;
|
|
134
134
|
// 必须检测是否是TranscludeToken
|
|
@@ -139,7 +139,7 @@ class ParameterToken extends fixedToken(Token) {
|
|
|
139
139
|
}
|
|
140
140
|
const root = Parser.parse(`{{:T|${key}=}}`, this.getAttribute('include'), 2, this.getAttribute('config')),
|
|
141
141
|
{childNodes: {length}, firstElementChild} = root;
|
|
142
|
-
if (length !== 1 || !firstElementChild?.matches('template#T') || firstElementChild.
|
|
142
|
+
if (length !== 1 || !firstElementChild?.matches('template#T') || firstElementChild.childNodes.length !== 2) {
|
|
143
143
|
throw new SyntaxError(`非法的模板参数名:${key}`);
|
|
144
144
|
}
|
|
145
145
|
const {lastElementChild} = firstElementChild,
|
package/src/syntax.js
CHANGED
|
@@ -38,8 +38,10 @@ class SyntaxToken extends Token {
|
|
|
38
38
|
afterBuild() {
|
|
39
39
|
const that = this,
|
|
40
40
|
/** @type {AstListener} */ syntaxListener = (e, data) => {
|
|
41
|
-
|
|
41
|
+
const pattern = that.#pattern;
|
|
42
|
+
if (!Parser.running && !pattern.test(that.text())) {
|
|
42
43
|
undo(e, data);
|
|
44
|
+
throw new Error(`不可修改 ${that.constructor.name} 的语法:/${pattern.source}/${pattern.flags}`);
|
|
43
45
|
}
|
|
44
46
|
};
|
|
45
47
|
this.addEventListener(['remove', 'insert', 'replace', 'text'], syntaxListener);
|
package/src/table/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const assert = require('assert/strict'),
|
|
4
|
+
{noWrap} = require('../../util/string'),
|
|
5
5
|
/** @type {Parser} */ Parser = require('../..'),
|
|
6
6
|
Token = require('..'), // eslint-disable-line no-unused-vars
|
|
7
7
|
TrToken = require('./tr'),
|
|
@@ -70,40 +70,45 @@ class TableToken extends TrToken {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
* @template {
|
|
73
|
+
* @template {TrToken|SyntaxToken} T
|
|
74
74
|
* @param {T} token
|
|
75
75
|
* @returns {T}
|
|
76
76
|
* @complexity `n`
|
|
77
77
|
*/
|
|
78
78
|
insertAt(token, i = this.childNodes.length) {
|
|
79
|
-
const previous = this.
|
|
79
|
+
const previous = this.children.at(i - 1),
|
|
80
80
|
{closingPattern} = TableToken;
|
|
81
|
-
if (token
|
|
81
|
+
if (token.type === 'td' && previous.type === 'tr') {
|
|
82
82
|
Parser.warn('改为将单元格插入当前行。');
|
|
83
83
|
return previous.appendChild(token);
|
|
84
84
|
} else if (!Parser.running && i === this.childNodes.length && token instanceof SyntaxToken
|
|
85
85
|
&& (token.getAttribute('pattern') !== closingPattern || !closingPattern.test(token.text()))
|
|
86
86
|
) {
|
|
87
|
-
throw new SyntaxError(`表格的闭合部分不符合语法!${token.toString()
|
|
87
|
+
throw new SyntaxError(`表格的闭合部分不符合语法!${noWrap(token.toString())}`);
|
|
88
88
|
}
|
|
89
89
|
return super.insertAt(token, i);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/** @complexity `n` */
|
|
93
|
-
close(syntax = '\n|}') {
|
|
93
|
+
close(syntax = '\n|}', halfParsed = false) {
|
|
94
|
+
halfParsed &&= Parser.running;
|
|
94
95
|
const config = this.getAttribute('config'),
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
accum = this.getAttribute('accum'),
|
|
97
|
+
inner = !halfParsed && Parser.parse(syntax, this.getAttribute('include'), 2, config),
|
|
98
|
+
{lastElementChild} = this,
|
|
99
|
+
{closingPattern} = TableToken;
|
|
100
|
+
if (!halfParsed && !closingPattern.test(inner.text())) {
|
|
101
|
+
throw new SyntaxError(`表格的闭合部分不符合语法!${noWrap(syntax)}`);
|
|
99
102
|
} else if (lastElementChild instanceof SyntaxToken) {
|
|
100
103
|
lastElementChild.replaceChildren(...inner.childNodes);
|
|
101
104
|
} else {
|
|
102
105
|
this.appendChild(Parser.run(() => {
|
|
103
|
-
const token = new SyntaxToken(
|
|
106
|
+
const token = new SyntaxToken(syntax, closingPattern, 'table-syntax', config, accum, {
|
|
104
107
|
'Stage-1': ':', '!ExtToken': '', TranscludeToken: ':',
|
|
105
108
|
});
|
|
106
|
-
|
|
109
|
+
if (inner) {
|
|
110
|
+
token.replaceChildren(...inner.childNodes);
|
|
111
|
+
}
|
|
107
112
|
return token;
|
|
108
113
|
}));
|
|
109
114
|
}
|
|
@@ -132,7 +137,7 @@ class TableToken extends TrToken {
|
|
|
132
137
|
*/
|
|
133
138
|
getNthRow(n, force = false, insert = false) {
|
|
134
139
|
if (typeof n !== 'number') {
|
|
135
|
-
typeError(
|
|
140
|
+
this.typeError('getNthRow', 'Number');
|
|
136
141
|
}
|
|
137
142
|
const nRows = this.getRowCount(),
|
|
138
143
|
isRow = super.getRowCount();
|
|
@@ -240,7 +245,7 @@ class TableToken extends TrToken {
|
|
|
240
245
|
*/
|
|
241
246
|
toRenderedCoords({row, column}) {
|
|
242
247
|
if (typeof row !== 'number' || typeof column !== 'number') {
|
|
243
|
-
typeError(
|
|
248
|
+
this.typeError('toRenderedCoords', 'Number');
|
|
244
249
|
}
|
|
245
250
|
const rowLayout = this.getLayout({row, column})[row],
|
|
246
251
|
x = rowLayout?.findIndex(coords => cmpCoords(coords, {row, column}) === 0);
|
|
@@ -253,7 +258,7 @@ class TableToken extends TrToken {
|
|
|
253
258
|
*/
|
|
254
259
|
toRawCoords({x, y}) {
|
|
255
260
|
if (typeof x !== 'number' || typeof y !== 'number') {
|
|
256
|
-
typeError(
|
|
261
|
+
this.typeError('toRawCoords', 'Number');
|
|
257
262
|
}
|
|
258
263
|
const rowLayout = this.getLayout({x, y})[y],
|
|
259
264
|
coords = rowLayout?.[x];
|
|
@@ -272,7 +277,7 @@ class TableToken extends TrToken {
|
|
|
272
277
|
*/
|
|
273
278
|
getFullRow(y) {
|
|
274
279
|
if (typeof y !== 'number') {
|
|
275
|
-
typeError(
|
|
280
|
+
this.typeError('getFullRow', 'Number');
|
|
276
281
|
}
|
|
277
282
|
const rows = this.getAllRows();
|
|
278
283
|
return new Map(
|
|
@@ -286,7 +291,7 @@ class TableToken extends TrToken {
|
|
|
286
291
|
*/
|
|
287
292
|
getFullCol(x) {
|
|
288
293
|
if (typeof x !== 'number') {
|
|
289
|
-
typeError(
|
|
294
|
+
this.typeError('getFullCol', 'Number');
|
|
290
295
|
}
|
|
291
296
|
const layout = this.getLayout(),
|
|
292
297
|
colLayout = layout.map(row => row[x]).filter(coords => coords),
|
|
@@ -412,14 +417,14 @@ class TableToken extends TrToken {
|
|
|
412
417
|
/** @complexity `n` */
|
|
413
418
|
#prependTableRow() {
|
|
414
419
|
const row = Parser.run(() => new TrToken('\n|-', undefined, this.getAttribute('config'))),
|
|
415
|
-
{
|
|
416
|
-
[,, plain] =
|
|
417
|
-
start =
|
|
418
|
-
/** @type {TdToken[]} */
|
|
419
|
-
index =
|
|
420
|
+
{children} = this,
|
|
421
|
+
[,, plain] = children,
|
|
422
|
+
start = plain?.isPlain() ? 3 : 2,
|
|
423
|
+
/** @type {TdToken[]} */ tdChildren = children.slice(start),
|
|
424
|
+
index = tdChildren.findIndex(({type}) => type !== 'td');
|
|
420
425
|
this.insertAt(row, index === -1 ? -1 : index + start);
|
|
421
426
|
Parser.run(() => {
|
|
422
|
-
for (const cell of
|
|
427
|
+
for (const cell of tdChildren.slice(0, index === -1 ? undefined : index)) {
|
|
423
428
|
if (cell.subtype !== 'caption') {
|
|
424
429
|
row.appendChild(cell);
|
|
425
430
|
}
|
|
@@ -438,7 +443,7 @@ class TableToken extends TrToken {
|
|
|
438
443
|
*/
|
|
439
444
|
insertTableRow(y, attr = {}, inner = undefined, subtype = 'td', innerAttr = {}) {
|
|
440
445
|
if (typeof attr !== 'object') {
|
|
441
|
-
typeError(
|
|
446
|
+
this.typeError('insertTableRow', 'Object');
|
|
442
447
|
}
|
|
443
448
|
let reference = this.getNthRow(y, false, true);
|
|
444
449
|
/** @type {TrToken & AttributeToken}} */
|
|
@@ -482,7 +487,7 @@ class TableToken extends TrToken {
|
|
|
482
487
|
*/
|
|
483
488
|
insertTableCol(x, inner, subtype = 'td', attr = {}) {
|
|
484
489
|
if (typeof x !== 'number') {
|
|
485
|
-
typeError(
|
|
490
|
+
this.typeError('insertTableCol', 'Number');
|
|
486
491
|
}
|
|
487
492
|
const layout = this.getLayout(),
|
|
488
493
|
rowLength = layout.map(({length}) => length),
|
|
@@ -565,7 +570,7 @@ class TableToken extends TrToken {
|
|
|
565
570
|
*/
|
|
566
571
|
mergeCells(xlim, ylim) {
|
|
567
572
|
if ([...xlim, ...ylim].some(arg => typeof arg !== 'number')) {
|
|
568
|
-
typeError(
|
|
573
|
+
this.typeError('mergeCells', 'Number');
|
|
569
574
|
}
|
|
570
575
|
const layout = this.getLayout(),
|
|
571
576
|
maxCol = Math.max(...layout.map(({length}) => length));
|
|
@@ -711,7 +716,7 @@ class TableToken extends TrToken {
|
|
|
711
716
|
*/
|
|
712
717
|
moveTableRowBefore(y, before) {
|
|
713
718
|
if (typeof y !== 'number' || typeof before !== 'number') {
|
|
714
|
-
typeError(
|
|
719
|
+
this.typeError('moveTableRowBefore', 'Number');
|
|
715
720
|
}
|
|
716
721
|
const layout = this.getLayout(),
|
|
717
722
|
/**
|
|
@@ -748,7 +753,7 @@ class TableToken extends TrToken {
|
|
|
748
753
|
*/
|
|
749
754
|
moveTableRowAfter(y, after) {
|
|
750
755
|
if (typeof y !== 'number' || typeof after !== 'number') {
|
|
751
|
-
typeError(
|
|
756
|
+
this.typeError('moveTableRowAfter', 'Number');
|
|
752
757
|
}
|
|
753
758
|
const layout = this.getLayout(),
|
|
754
759
|
afterToken = this.getNthRow(after),
|
|
@@ -797,7 +802,7 @@ class TableToken extends TrToken {
|
|
|
797
802
|
*/
|
|
798
803
|
#moveCol(x, reference, after = false) {
|
|
799
804
|
if (typeof x !== 'number' || typeof reference !== 'number') {
|
|
800
|
-
typeError(
|
|
805
|
+
this.typeError(`moveTableCol${after ? 'After' : 'Before'}`, 'Number');
|
|
801
806
|
}
|
|
802
807
|
const layout = this.getLayout(),
|
|
803
808
|
/** @type {(rowLayout: TableCoords[], i: number, oneCol?: boolean) => boolean} */
|