wikiparser-node 1.30.0 → 1.32.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 +9 -4
- package/bundle/bundle-es8.min.js +28 -29
- package/bundle/bundle-lsp.min.js +34 -34
- package/bundle/bundle.min.js +23 -23
- package/dist/addon/attribute.js +166 -0
- package/dist/addon/link.js +92 -0
- package/dist/addon/table.js +12 -3
- package/dist/addon/token.js +5 -316
- package/dist/addon/transclude.js +11 -8
- package/dist/base.d.mts +13 -4
- package/dist/base.d.ts +13 -4
- package/dist/base.js +2 -0
- package/dist/base.mjs +2 -0
- package/dist/bin/config.js +1 -1
- package/dist/index.d.ts +34 -5
- package/dist/index.js +100 -88
- package/dist/internal.d.ts +6 -1
- package/dist/lib/document.d.ts +9 -7
- package/dist/lib/document.js +91 -66
- package/dist/lib/element.d.ts +8 -8
- package/dist/lib/element.js +52 -50
- package/dist/lib/lintConfig.js +6 -2
- package/dist/lib/lsp.js +120 -56
- package/dist/lib/node.js +21 -15
- package/dist/lib/redirectMap.js +1 -1
- package/dist/lib/text.js +17 -14
- package/dist/lib/title.d.ts +11 -0
- package/dist/lib/title.js +37 -13
- package/dist/mixin/elementLike.js +2 -3
- package/dist/mixin/syntax.js +13 -7
- package/dist/parser/commentAndExt.js +4 -4
- package/dist/parser/hrAndDoubleUnderscore.js +11 -9
- package/dist/parser/selector.js +16 -41
- package/dist/render/expand.js +216 -0
- package/dist/render/extension.js +141 -0
- package/dist/render/html.js +91 -0
- package/dist/render/magicWords.js +768 -0
- package/dist/render/syntaxhighlight.js +415 -0
- package/dist/src/arg.js +8 -1
- package/dist/src/atom.js +2 -0
- package/dist/src/attribute.d.ts +5 -0
- package/dist/src/attribute.js +59 -104
- package/dist/src/attributes.d.ts +5 -0
- package/dist/src/attributes.js +17 -51
- package/dist/src/converter.js +4 -2
- package/dist/src/converterFlags.js +8 -6
- package/dist/src/converterRule.js +19 -15
- package/dist/src/extLink.js +2 -2
- package/dist/src/heading.d.ts +1 -1
- package/dist/src/heading.js +5 -3
- package/dist/src/imageParameter.d.ts +0 -1
- package/dist/src/imageParameter.js +34 -24
- package/dist/src/index.d.ts +5 -5
- package/dist/src/index.js +37 -27
- package/dist/src/link/base.js +26 -36
- package/dist/src/link/category.d.ts +8 -1
- package/dist/src/link/category.js +104 -38
- package/dist/src/link/file.js +21 -8
- package/dist/src/link/galleryImage.js +2 -3
- package/dist/src/link/index.d.ts +1 -1
- package/dist/src/link/index.js +12 -33
- package/dist/src/multiLine/index.js +1 -1
- package/dist/src/nowiki/comment.js +1 -1
- package/dist/src/nowiki/doubleUnderscore.js +4 -2
- package/dist/src/nowiki/index.js +34 -31
- package/dist/src/nowiki/list.js +3 -1
- package/dist/src/nowiki/listBase.d.ts +2 -1
- package/dist/src/nowiki/listBase.js +22 -8
- package/dist/src/nowiki/quote.d.ts +1 -1
- package/dist/src/nowiki/quote.js +3 -3
- package/dist/src/onlyinclude.js +1 -1
- package/dist/src/paramLine.js +2 -0
- package/dist/src/parameter.js +1 -1
- package/dist/src/redirect.js +1 -1
- package/dist/src/table/base.js +5 -4
- package/dist/src/table/index.d.ts +1 -6
- package/dist/src/table/index.js +7 -16
- package/dist/src/table/td.js +12 -9
- package/dist/src/tag/html.js +2 -3
- package/dist/src/tag/index.js +8 -6
- package/dist/src/tagPair/ext.js +18 -41
- package/dist/src/tagPair/index.js +6 -4
- package/dist/src/tagPair/translate.d.ts +1 -0
- package/dist/src/tagPair/translate.js +10 -3
- package/dist/src/transclude.js +18 -21
- package/dist/util/constants.js +9 -3
- package/dist/util/debug.js +72 -5
- package/dist/util/diff.js +17 -15
- package/dist/util/html.js +14 -5
- package/dist/util/selector.js +37 -0
- package/dist/util/sharable.d.mts +4 -1
- package/dist/util/sharable.js +7 -7
- package/dist/util/sharable.mjs +7 -7
- package/dist/util/string.js +51 -20
- package/extensions/dist/base.js +2 -2
- package/extensions/editor.css +1 -1
- package/i18n/en.json +1 -0
- package/i18n/zh-hans.json +30 -29
- package/i18n/zh-hant.json +31 -30
- package/package.json +23 -16
- package/dist/addon/magicWords.js +0 -132
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint @stylistic/operator-linebreak: [2, "before", {overrides: {"=": "after"}}] */
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const constants_1 = require("../util/constants");
|
|
8
|
+
const debug_1 = require("../util/debug");
|
|
9
|
+
const string_1 = require("../util/string");
|
|
10
|
+
const index_1 = __importDefault(require("../index"));
|
|
11
|
+
const document_1 = require("../lib/document");
|
|
12
|
+
const attribute_1 = require("../src/attribute");
|
|
13
|
+
const attributes_1 = require("../src/attributes");
|
|
14
|
+
const atom_1 = require("../src/atom");
|
|
15
|
+
attribute_1.AttributeToken.prototype.setValue =
|
|
16
|
+
/** @implements */
|
|
17
|
+
function (value) {
|
|
18
|
+
if (value === false) {
|
|
19
|
+
this.remove();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
else if (value === true) {
|
|
23
|
+
this.setAttribute('equal', '');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const { type, lastChild } = this;
|
|
27
|
+
if (type === 'ext-attr' && value.includes('>')) {
|
|
28
|
+
throw new RangeError('Attributes of an extension tag cannot contain ">"!');
|
|
29
|
+
}
|
|
30
|
+
else if (value.includes('"') && value.includes(`'`)) {
|
|
31
|
+
throw new RangeError('Attribute values cannot contain single and double quotes simultaneously!');
|
|
32
|
+
}
|
|
33
|
+
const { childNodes } = index_1.default.parseWithRef(value, this, attribute_1.stages[type] + 1);
|
|
34
|
+
lastChild.safeReplaceChildren(childNodes);
|
|
35
|
+
if (value.includes('"')) {
|
|
36
|
+
this.setAttribute('quotes', [`'`, `'`]);
|
|
37
|
+
}
|
|
38
|
+
else if (value.includes(`'`) || !this.getAttribute('quotes')[0]) {
|
|
39
|
+
this.setAttribute('quotes', ['"', '"']);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
this.close();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
attribute_1.AttributeToken.prototype.rename =
|
|
46
|
+
/** @implements */
|
|
47
|
+
function (key) {
|
|
48
|
+
const { type, name, tag, firstChild } = this;
|
|
49
|
+
if (name === 'title' || name === 'alt' && tag === 'img') {
|
|
50
|
+
throw new Error(`${name} attribute cannot be renamed!`);
|
|
51
|
+
}
|
|
52
|
+
const { childNodes } = index_1.default.parseWithRef(key, this, attribute_1.stages[type] + 1);
|
|
53
|
+
firstChild.safeReplaceChildren(childNodes);
|
|
54
|
+
};
|
|
55
|
+
attribute_1.AttributeToken.prototype.css =
|
|
56
|
+
/** @implements */
|
|
57
|
+
function (key, value) {
|
|
58
|
+
const { name, lastChild } = this;
|
|
59
|
+
if (name !== 'style') {
|
|
60
|
+
throw new Error('Not a style attribute!');
|
|
61
|
+
}
|
|
62
|
+
else if (lastChild.length !== 1 || lastChild.firstChild.type !== 'text') {
|
|
63
|
+
throw new Error('Complex style attribute!');
|
|
64
|
+
}
|
|
65
|
+
const cssLSP = (0, document_1.loadCssLSP)();
|
|
66
|
+
if (!cssLSP) {
|
|
67
|
+
throw new Error('CSS language service is not available!');
|
|
68
|
+
}
|
|
69
|
+
const doc = new document_1.EmbeddedCSSDocument(this.getRootNode(), lastChild), styleSheet = doc.styleSheet, { children: [{ declarations }] } = styleSheet, declaration = declarations.children?.filter(({ property }) => property.getText() === key) ?? [];
|
|
70
|
+
if (value === undefined) {
|
|
71
|
+
return declaration.at(-1)?.value.getText();
|
|
72
|
+
}
|
|
73
|
+
else if (typeof value === 'number') {
|
|
74
|
+
value = String(value);
|
|
75
|
+
}
|
|
76
|
+
const style = styleSheet.getText().slice(0, -1);
|
|
77
|
+
if (!value) {
|
|
78
|
+
if (declaration.length === declarations.children?.length) {
|
|
79
|
+
this.setValue('');
|
|
80
|
+
}
|
|
81
|
+
else if (declaration.length > 0) {
|
|
82
|
+
let output = '', start = doc.pre.length;
|
|
83
|
+
for (const { offset, length } of declaration) {
|
|
84
|
+
output += style.slice(start, offset);
|
|
85
|
+
start = offset + length;
|
|
86
|
+
}
|
|
87
|
+
output += style.slice(start);
|
|
88
|
+
this.setValue(output.replace(/^\s*;\s*|;\s*(?=;)/gu, ''));
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const hasQuote = value.includes('"'), [quot] = this.getAttribute('quotes');
|
|
93
|
+
if (quot && value.includes(quot) || hasQuote && value.includes(`'`)) {
|
|
94
|
+
const quote = quot || '"';
|
|
95
|
+
throw new RangeError(`Please consider replacing \`${quote}\` with \`${quote === '"' ? `'` : '"'}\`!`);
|
|
96
|
+
}
|
|
97
|
+
else if (declaration.length > 0) {
|
|
98
|
+
const { offset, length } = declaration.at(-1).value;
|
|
99
|
+
this.setValue(style.slice(doc.pre.length, offset) + value + style.slice(offset + length));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
this.setValue(`${style.slice(doc.pre.length)}${/;\s*$/u.test(style) ? '' : '; '}${key}: ${value}`);
|
|
103
|
+
}
|
|
104
|
+
return undefined;
|
|
105
|
+
};
|
|
106
|
+
attributes_1.AttributesToken.prototype.sanitize =
|
|
107
|
+
/** @implements */
|
|
108
|
+
function () {
|
|
109
|
+
let dirty = false;
|
|
110
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
111
|
+
const child = this.childNodes[i];
|
|
112
|
+
if (child instanceof atom_1.AtomToken && child.text().trim()) {
|
|
113
|
+
dirty = true;
|
|
114
|
+
this.removeAt(i);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (!debug_1.Shadow.running && dirty) {
|
|
118
|
+
index_1.default.warn('AttributesToken.sanitize will remove invalid attributes!');
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
attributes_1.AttributesToken.prototype.setAttr =
|
|
122
|
+
/** @implements */
|
|
123
|
+
function (keyOrProp, value) {
|
|
124
|
+
if (typeof keyOrProp === 'object') {
|
|
125
|
+
for (const [key, val] of Object.entries(keyOrProp)) {
|
|
126
|
+
this.setAttr(key, val);
|
|
127
|
+
}
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const { type, name } = this;
|
|
131
|
+
if (type === 'ext-attrs' && typeof value === 'string' && value.includes('>')) {
|
|
132
|
+
throw new RangeError('Attributes of an extension tag cannot contain ">"!');
|
|
133
|
+
}
|
|
134
|
+
const key = (0, string_1.trimLc)(keyOrProp), attr = this.getAttrToken(key);
|
|
135
|
+
if (attr) {
|
|
136
|
+
attr.setValue(value);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
else if (value === false) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
// @ts-expect-error abstract class
|
|
143
|
+
const token = debug_1.Shadow.run(() => new attribute_1.AttributeToken((0, attributes_1.toAttributeType)(type), name, key, value === true ? '' : '=', value === true ? '' : value, ['"', '"'], this.getAttribute('config')));
|
|
144
|
+
this.insertAt(token);
|
|
145
|
+
};
|
|
146
|
+
attributes_1.AttributesToken.prototype.toggleAttr =
|
|
147
|
+
/** @implements */
|
|
148
|
+
function (key, force) {
|
|
149
|
+
key = (0, string_1.trimLc)(key);
|
|
150
|
+
const attr = this.getAttrToken(key);
|
|
151
|
+
if (attr && attr.getValue() !== true) {
|
|
152
|
+
throw new RangeError(`${key} attribute is not Boolean!`);
|
|
153
|
+
}
|
|
154
|
+
else if (attr) {
|
|
155
|
+
attr.setValue(force === true);
|
|
156
|
+
}
|
|
157
|
+
else if (force !== false) {
|
|
158
|
+
this.setAttr(key, true);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
attributes_1.AttributesToken.prototype.css =
|
|
162
|
+
/** @implements */
|
|
163
|
+
function (key, value) {
|
|
164
|
+
return this.getAttrToken('style')?.css(key, value);
|
|
165
|
+
};
|
|
166
|
+
constants_1.classes['ExtendedAttributeToken'] = __filename;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint @stylistic/operator-linebreak: [2, "before", {overrides: {"=": "after"}}] */
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const constants_1 = require("../util/constants");
|
|
8
|
+
const debug_1 = require("../util/debug");
|
|
9
|
+
const string_1 = require("../util/string");
|
|
10
|
+
const index_1 = __importDefault(require("../index"));
|
|
11
|
+
const base_1 = require("../src/link/base");
|
|
12
|
+
const index_2 = require("../src/link/index");
|
|
13
|
+
const atom_1 = require("../src/atom");
|
|
14
|
+
base_1.LinkBaseToken.prototype.setTarget =
|
|
15
|
+
/** @implements */
|
|
16
|
+
function (link) {
|
|
17
|
+
const { childNodes } = index_1.default.parseWithRef(link, this, 2), token = debug_1.Shadow.run(() => new atom_1.AtomToken(undefined, 'link-target', this.getAttribute('config'), [], { 'Stage-2': ':', '!ExtToken': '', '!HeadingToken': '' }));
|
|
18
|
+
token.concat(childNodes); // eslint-disable-line unicorn/prefer-spread
|
|
19
|
+
this.firstChild.safeReplaceWith(token);
|
|
20
|
+
};
|
|
21
|
+
base_1.LinkBaseToken.prototype.setFragment =
|
|
22
|
+
/** @implements */
|
|
23
|
+
function (fragment) {
|
|
24
|
+
const { type, name } = this;
|
|
25
|
+
if (fragment === undefined || (0, debug_1.isLink)(type)) {
|
|
26
|
+
fragment &&= (0, string_1.encode)(fragment);
|
|
27
|
+
this.setTarget(name + (fragment === undefined ? '' : `#${fragment}`));
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
base_1.LinkBaseToken.prototype.setLinkText =
|
|
31
|
+
/** @implements */
|
|
32
|
+
function (linkStr) {
|
|
33
|
+
const { childNodes, lastChild, length } = this;
|
|
34
|
+
if (linkStr === undefined) {
|
|
35
|
+
childNodes[1]?.remove();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const root = index_1.default.parseWithRef(linkStr, this);
|
|
39
|
+
if (length === 1) {
|
|
40
|
+
root.type = 'link-text';
|
|
41
|
+
root.setAttribute('acceptable', {
|
|
42
|
+
'Stage-5': ':', QuoteToken: ':', ConverterToken: ':',
|
|
43
|
+
});
|
|
44
|
+
this.insertAt(root);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
lastChild.safeReplaceChildren(root.childNodes);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
index_2.LinkToken.prototype.setLangLink =
|
|
51
|
+
/** @implements */
|
|
52
|
+
function (lang, link) {
|
|
53
|
+
link = link.trim();
|
|
54
|
+
/* istanbul ignore if */
|
|
55
|
+
if (link.startsWith('#')) {
|
|
56
|
+
throw new SyntaxError('An interlanguage link cannot be fragment only!');
|
|
57
|
+
}
|
|
58
|
+
this.setTarget(lang + (link.startsWith(':') ? '' : ':') + link);
|
|
59
|
+
};
|
|
60
|
+
index_2.LinkToken.prototype.asSelfLink =
|
|
61
|
+
/** @implements */
|
|
62
|
+
function (fragment) {
|
|
63
|
+
fragment ??= this.fragment;
|
|
64
|
+
/* istanbul ignore if */
|
|
65
|
+
if (!fragment?.trim()) {
|
|
66
|
+
throw new RangeError('LinkToken.asSelfLink method must specify a non-empty fragment!');
|
|
67
|
+
}
|
|
68
|
+
this.setTarget(`#${(0, string_1.encode)(fragment)}`);
|
|
69
|
+
};
|
|
70
|
+
index_2.LinkToken.prototype.pipeTrick =
|
|
71
|
+
/** @implements */
|
|
72
|
+
function () {
|
|
73
|
+
const linkText = this.firstChild.text();
|
|
74
|
+
/* istanbul ignore if */
|
|
75
|
+
if (linkText.includes('#') || linkText.includes('%')) {
|
|
76
|
+
throw new Error('Pipe trick cannot be used with "#" or "%"!');
|
|
77
|
+
}
|
|
78
|
+
const m1 = /^:?(?:[ \w\x80-\xFF-]+:)?([^(]+?) ?\(.+\)$/u.exec(linkText);
|
|
79
|
+
if (m1) {
|
|
80
|
+
this.setLinkText(m1[1]);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const m2 = /^:?(?:[ \w\x80-\xFF-]+:)?([^(]+?) ?(.+)$/u.exec(linkText);
|
|
84
|
+
if (m2) {
|
|
85
|
+
this.setLinkText(m2[1]);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const m3 = /^:?(?:[ \w\x80-\xFF-]+:)?(.*?)(?: ?(?<!\()\(.+\))?(?:(?:, |,|، ).|$)/u
|
|
89
|
+
.exec(linkText);
|
|
90
|
+
this.setLinkText(m3[1]);
|
|
91
|
+
};
|
|
92
|
+
constants_1.classes['ExtendedLinkToken'] = __filename;
|
package/dist/addon/table.js
CHANGED
|
@@ -106,6 +106,15 @@ const getMaxCol = (layout) => {
|
|
|
106
106
|
}
|
|
107
107
|
return maxCol;
|
|
108
108
|
};
|
|
109
|
+
index_2.TableToken.prototype.getNthCell =
|
|
110
|
+
/** @implements */
|
|
111
|
+
function (coords) {
|
|
112
|
+
let rawCoords = coords;
|
|
113
|
+
if (coords.row === undefined) {
|
|
114
|
+
rawCoords = this.toRawCoords(coords);
|
|
115
|
+
}
|
|
116
|
+
return rawCoords && this.getNthRow(rawCoords.row, false, false)?.getNthCol(rawCoords.column);
|
|
117
|
+
};
|
|
109
118
|
index_2.TableToken.prototype.printLayout =
|
|
110
119
|
/** @implements */
|
|
111
120
|
function () {
|
|
@@ -144,7 +153,7 @@ index_2.TableToken.prototype.getFullCol =
|
|
|
144
153
|
/** @implements */
|
|
145
154
|
function (x) {
|
|
146
155
|
const layout = this.getLayout(), rows = this.getAllRows();
|
|
147
|
-
return new Map(layout.map(row => row[x]).filter(
|
|
156
|
+
return new Map(layout.map(row => row[x]).filter(coords => coords !== undefined).map(coords => [rows[coords.row].getNthCol(coords.column), layout[coords.row][x - 1] !== coords]));
|
|
148
157
|
};
|
|
149
158
|
index_2.TableToken.prototype.formatTableRow =
|
|
150
159
|
/** @implements */
|
|
@@ -470,7 +479,7 @@ index_2.TableToken.prototype.moveTableRowAfter =
|
|
|
470
479
|
}
|
|
471
480
|
}
|
|
472
481
|
if (afterToken === this) {
|
|
473
|
-
const index = this.childNodes.slice(2).findIndex(
|
|
482
|
+
const index = this.childNodes.slice(2).findIndex(debug_1.isRowEnd);
|
|
474
483
|
this.insertAt(rowToken, index + 2);
|
|
475
484
|
}
|
|
476
485
|
else {
|
|
@@ -509,7 +518,7 @@ index_2.TableToken.prototype.moveCol =
|
|
|
509
518
|
if (start) {
|
|
510
519
|
const col = rowLayout.slice(reference + Number(after)).find(({ row }) => row === i)?.column;
|
|
511
520
|
rowToken.insertBefore(token, col === undefined
|
|
512
|
-
? rowToken.childNodes.slice(2).find(
|
|
521
|
+
? rowToken.childNodes.slice(2).find(debug_1.isRowEnd)
|
|
513
522
|
: rowToken.getNthCol(col));
|
|
514
523
|
}
|
|
515
524
|
}
|
package/dist/addon/token.js
CHANGED
|
@@ -1,329 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const fs_1 = __importDefault(require("fs"));
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
3
|
const constants_1 = require("../util/constants");
|
|
9
4
|
const debug_1 = require("../util/debug");
|
|
10
|
-
const
|
|
11
|
-
const index_1 = __importDefault(require("../index"));
|
|
12
|
-
const index_2 = require("../src/index");
|
|
5
|
+
const index_1 = require("../src/index");
|
|
13
6
|
const comment_1 = require("../src/nowiki/comment");
|
|
14
7
|
const include_1 = require("../src/tagPair/include");
|
|
15
8
|
const ext_1 = require("../src/tagPair/ext");
|
|
16
9
|
const html_1 = require("../src/tag/html");
|
|
17
10
|
const attributes_1 = require("../src/attributes");
|
|
18
|
-
|
|
19
|
-
const blockElems = 'table|h1|h2|h3|h4|h5|h6|pre|p|ul|ol|dl', antiBlockElems = 'td|th', solvedMagicWords = new Set([
|
|
20
|
-
'if',
|
|
21
|
-
'ifeq',
|
|
22
|
-
'ifexist',
|
|
23
|
-
'switch',
|
|
24
|
-
]);
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
26
|
-
/<(?:table|\/(?:td|th)|\/?(?:tr|caption|dt|dd|li))\b/iu;
|
|
27
|
-
const openRegex = new RegExp(String.raw `<(?:${blockElems}|\/(?:${antiBlockElems})|\/?(?:tr|caption|dt|dd|li))\b`, 'iu');
|
|
28
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
29
|
-
/<(?:\/(?:h1|h2)|td|th|\/?(?:center|blockquote|div|hr|figure))\b/iu;
|
|
30
|
-
const closeRegex = new RegExp(String.raw `<(?:\/(?:${blockElems})|${antiBlockElems}|\/?(?:center|blockquote|div|hr|figure))\b`, 'iu');
|
|
31
|
-
/**
|
|
32
|
-
* 隐式换行
|
|
33
|
-
* @param str 字符串
|
|
34
|
-
* @param prev 前一个字符
|
|
35
|
-
*/
|
|
36
|
-
const implicitNewLine = (str, prev) => prev + (prev !== '\n' && /^(?:\{\||[:;#*])/u.test(str) ? `\n${str}` : str);
|
|
37
|
-
/**
|
|
38
|
-
* 比较两个字符串是否相等
|
|
39
|
-
* @param a
|
|
40
|
-
* @param b
|
|
41
|
-
*/
|
|
42
|
-
const cmp = (a, b) => a === b || Boolean(a && b) && Number(a) === Number(b);
|
|
43
|
-
/**
|
|
44
|
-
* 解析 if/ifexist/ifeq 解析器函数
|
|
45
|
-
* @param accum
|
|
46
|
-
* @param prev 解析器函数前的字符串
|
|
47
|
-
* @param effective 生效的参数
|
|
48
|
-
*/
|
|
49
|
-
const parseIf = (accum, prev, effective) => {
|
|
50
|
-
if (effective) {
|
|
51
|
-
// @ts-expect-error sparse array
|
|
52
|
-
accum[accum.indexOf(effective.lastChild)] = undefined;
|
|
53
|
-
return implicitNewLine(effective.value, prev);
|
|
54
|
-
}
|
|
55
|
-
return prev;
|
|
56
|
-
};
|
|
57
|
-
/**
|
|
58
|
-
* 展开模板
|
|
59
|
-
* @param wikitext
|
|
60
|
-
* @param page 页面名称
|
|
61
|
-
* @param config
|
|
62
|
-
* @param include
|
|
63
|
-
* @param context 模板调用环境
|
|
64
|
-
* @param now 当前时间
|
|
65
|
-
* @param accum
|
|
66
|
-
* @param stack 模板调用栈
|
|
67
|
-
*/
|
|
68
|
-
const expand = (wikitext, page, config, include, context, now = index_1.default.now ?? new Date(), accum = [], stack = []) => {
|
|
69
|
-
const n = accum.length, token = new index_2.Token(wikitext, { ...config, inExt: true }, accum);
|
|
70
|
-
token.type = 'root';
|
|
71
|
-
token.pageName = page;
|
|
72
|
-
token.parseOnce(0, include);
|
|
73
|
-
if (context !== false) {
|
|
74
|
-
token.setText((0, string_1.removeCommentLine)(token.firstChild.toString(), true));
|
|
75
|
-
}
|
|
76
|
-
token.parseOnce();
|
|
77
|
-
for (const plain of [...accum.slice(n), token]) {
|
|
78
|
-
if (plain.length !== 1 || plain.firstChild.type !== 'text') {
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
const { data } = plain.firstChild;
|
|
82
|
-
if (!/\0\d+t\x7F/u.test(data)) {
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
const expanded = data.replace(/([^\x7F]?)\0(\d+)t\x7F/gu, (m, prev, i) => {
|
|
86
|
-
const target = accum[i], { type, name, length, firstChild: f, childNodes } = target, isTemplate = type === 'template', args = childNodes.slice(1);
|
|
87
|
-
if (type === 'arg') {
|
|
88
|
-
const arg = (0, string_1.removeCommentLine)(f.toString()).trim();
|
|
89
|
-
if (/\0\d+t\x7F/u.test(arg)) {
|
|
90
|
-
return m;
|
|
91
|
-
}
|
|
92
|
-
else if (!context || !context.hasArg(arg)) {
|
|
93
|
-
const effective = target.childNodes[1] ?? target;
|
|
94
|
-
// @ts-expect-error sparse array
|
|
95
|
-
accum[accum.indexOf(length === 1 ? f : effective)] = undefined;
|
|
96
|
-
return prev + effective.toString();
|
|
97
|
-
}
|
|
98
|
-
// @ts-expect-error sparse array
|
|
99
|
-
accum[accum.indexOf(context.getArg(arg).lastChild)] = undefined;
|
|
100
|
-
return prev + context.getValue(arg);
|
|
101
|
-
}
|
|
102
|
-
else if (isTemplate || name === 'int') {
|
|
103
|
-
if (context === false) {
|
|
104
|
-
return m;
|
|
105
|
-
}
|
|
106
|
-
const nameToken = isTemplate ? f : args[0], key = (0, string_1.removeComment)(nameToken.toString()), fallback = isTemplate ? m : `${prev}⧼${key}⧽`, { title, valid } = index_1.default.normalizeTitle((isTemplate ? '' : 'MediaWiki:') + key, 10, include, config, { halfParsed: true, temporary: true, page });
|
|
107
|
-
if (!valid) {
|
|
108
|
-
// @ts-expect-error sparse array
|
|
109
|
-
accum[accum.indexOf(target)] = undefined;
|
|
110
|
-
// @ts-expect-error sparse array
|
|
111
|
-
accum[accum.indexOf(f)] = undefined;
|
|
112
|
-
return isTemplate ? prev + target.toString() : fallback;
|
|
113
|
-
}
|
|
114
|
-
else if (!index_1.default.templates.has(title)) {
|
|
115
|
-
if (index_1.default.templateDir === undefined) {
|
|
116
|
-
return fallback;
|
|
117
|
-
}
|
|
118
|
-
else if (!path_1.default.isAbsolute(index_1.default.templateDir)) {
|
|
119
|
-
index_1.default.templateDir = path_1.default.join(__dirname, '..', '..', index_1.default.templateDir);
|
|
120
|
-
}
|
|
121
|
-
const file = fs_1.default.readdirSync(index_1.default.templateDir, { withFileTypes: true, recursive: true })
|
|
122
|
-
.filter(dirent => dirent.isFile())
|
|
123
|
-
.find(({ name: fl, parentPath }) => {
|
|
124
|
-
const t = path_1.default.relative(index_1.default.templateDir, path_1.default.join(parentPath, fl.replace(/\.(?:wiki|txt)$/iu, ''))).replaceAll('꞉', ':');
|
|
125
|
-
try {
|
|
126
|
-
return decodeURIComponent(t) === title;
|
|
127
|
-
}
|
|
128
|
-
catch {
|
|
129
|
-
return t === title;
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
if (!file) {
|
|
133
|
-
return fallback;
|
|
134
|
-
}
|
|
135
|
-
index_1.default.templates.set(title, fs_1.default.readFileSync(path_1.default.join(file.parentPath, file.name), 'utf8'));
|
|
136
|
-
}
|
|
137
|
-
else if (stack.includes(title)) {
|
|
138
|
-
return `${prev}<span class="error">Template loop detected: [[${title}]]</span>`;
|
|
139
|
-
}
|
|
140
|
-
let template = index_1.default.templates.get(title);
|
|
141
|
-
if (!isTemplate) {
|
|
142
|
-
for (let j = 1; j < args.length; j++) {
|
|
143
|
-
template = template.replaceAll(`$${j}`, (0, string_1.removeComment)(args[j].toString()));
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return implicitNewLine(expand(template, title, config, true, target, now, accum, [...stack, title]).toString(), prev);
|
|
147
|
-
}
|
|
148
|
-
else if (index_1.default.functionHooks.has(name)) {
|
|
149
|
-
return context === false ? m : prev + index_1.default.functionHooks.get(name)(target, context || undefined);
|
|
150
|
-
}
|
|
151
|
-
else if (magicWords_1.expandedMagicWords.has(name)) {
|
|
152
|
-
return context === false ? m : `${prev}${(0, magicWords_1.expandMagicWord)(name, now, config, args)}`;
|
|
153
|
-
}
|
|
154
|
-
else if (!solvedMagicWords.has(name)) {
|
|
155
|
-
return m;
|
|
156
|
-
}
|
|
157
|
-
else if (length < 3 || name === 'ifeq' && length === 3) {
|
|
158
|
-
return prev;
|
|
159
|
-
}
|
|
160
|
-
const var1 = (0, string_1.decodeHtml)(args[0].value), var2 = (0, string_1.decodeHtml)(args[1].value), known = !/\0\d+t\x7F/u.test(var1);
|
|
161
|
-
if (known && (name === 'if' || name === 'ifexist')) {
|
|
162
|
-
let bool = Boolean(var1);
|
|
163
|
-
if (name === 'ifexist') {
|
|
164
|
-
const { valid, interwiki } = index_1.default.normalizeTitle(var1, 0, include, config, { halfParsed: true, temporary: true, page: '' });
|
|
165
|
-
bool = valid && !interwiki;
|
|
166
|
-
}
|
|
167
|
-
return parseIf(accum, prev, args[bool ? 1 : 2]);
|
|
168
|
-
}
|
|
169
|
-
else if (known && name === 'ifeq' && !/\0\d+t\x7F/u.test(var2)) {
|
|
170
|
-
return parseIf(accum, prev, args[cmp(var1, var2) ? 2 : 3]);
|
|
171
|
-
}
|
|
172
|
-
else if (known && name === 'switch') {
|
|
173
|
-
let defaultVal = '', j = 2,
|
|
174
|
-
/**
|
|
175
|
-
* - `1` 表示默认值
|
|
176
|
-
* - `2` 表示匹配值
|
|
177
|
-
*/
|
|
178
|
-
found = 0, transclusion = false, // eslint-disable-line no-useless-assignment
|
|
179
|
-
defaultParam;
|
|
180
|
-
for (; j < length; j++) {
|
|
181
|
-
const { anon, value, lastChild, name: option } = args[j - 1];
|
|
182
|
-
transclusion = /\0\d+t\x7F/u.test(anon ? value : option);
|
|
183
|
-
if (anon) {
|
|
184
|
-
if (j === length - 1) { // 位于最后的匿名参数是默认值
|
|
185
|
-
defaultParam = lastChild;
|
|
186
|
-
defaultVal = value;
|
|
187
|
-
}
|
|
188
|
-
else if (transclusion) { // 不支持复杂参数
|
|
189
|
-
break;
|
|
190
|
-
}
|
|
191
|
-
else if (cmp(var1, (0, string_1.decodeHtml)(value))) { // 下一个命名参数视为匹配值
|
|
192
|
-
found = 2;
|
|
193
|
-
}
|
|
194
|
-
else if (value === '#default' && found !== 2) { // 下一个命名参数视为默认值
|
|
195
|
-
found = 1;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
else if (transclusion) { // 不支持复杂参数
|
|
199
|
-
break;
|
|
200
|
-
}
|
|
201
|
-
else if (found === 2 || cmp(var1, (0, string_1.decodeHtml)(option))) { // 第一个匹配值
|
|
202
|
-
// @ts-expect-error sparse array
|
|
203
|
-
accum[accum.indexOf(lastChild)] = undefined;
|
|
204
|
-
return implicitNewLine(value, prev);
|
|
205
|
-
}
|
|
206
|
-
else if (found === 1 || option.toLowerCase() === '#default') { // 更新默认值
|
|
207
|
-
defaultParam = lastChild;
|
|
208
|
-
defaultVal = value;
|
|
209
|
-
found = 0;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
if (j === length) { // 不含复杂参数
|
|
213
|
-
if (defaultParam) {
|
|
214
|
-
// @ts-expect-error sparse array
|
|
215
|
-
accum[accum.indexOf(defaultParam)] = undefined;
|
|
216
|
-
}
|
|
217
|
-
return implicitNewLine(defaultVal, prev);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
return m;
|
|
221
|
-
});
|
|
222
|
-
plain.setText(expanded);
|
|
223
|
-
if (plain.type === 'parameter-key') {
|
|
224
|
-
plain.parentNode.trimName((0, string_1.removeCommentLine)(expanded));
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
return token;
|
|
228
|
-
};
|
|
229
|
-
/**
|
|
230
|
-
* 展开指定节点的模板
|
|
231
|
-
* @param token 目标节点
|
|
232
|
-
* @param context 模板调用环境
|
|
233
|
-
*/
|
|
234
|
-
const expandToken = (token, context) => expand(token.toString(), token.pageName, token.getAttribute('config'), token.getAttribute('include'), context);
|
|
235
|
-
index_2.Token.prototype.expand = /** @implements */ function () {
|
|
236
|
-
return debug_1.Shadow.run(() => expandToken(this).parse());
|
|
237
|
-
};
|
|
238
|
-
index_2.Token.prototype.solveConst = /** @implements */ function () {
|
|
239
|
-
return debug_1.Shadow.run(() => expandToken(this, false).parse());
|
|
240
|
-
};
|
|
241
|
-
index_2.Token.prototype.toHtml = /** @implements */ function () {
|
|
242
|
-
const { viewOnly } = index_1.default;
|
|
243
|
-
let html;
|
|
244
|
-
if (this.type === 'root') {
|
|
245
|
-
index_1.default.viewOnly = true;
|
|
246
|
-
const expanded = debug_1.Shadow.run(() => expandToken(this).parse(undefined, false, true));
|
|
247
|
-
index_1.default.viewOnly = false;
|
|
248
|
-
constants_1.states.set(expanded, { headings: new Set() });
|
|
249
|
-
const lines = expanded.toHtmlInternal().split('\n');
|
|
250
|
-
let output = '', inBlockElem = false, pendingPTag = false, inBlockquote = false, lastParagraph = '';
|
|
251
|
-
const /** @ignore */ closeParagraph = () => {
|
|
252
|
-
if (lastParagraph) {
|
|
253
|
-
const result = `</${lastParagraph}>\n`;
|
|
254
|
-
lastParagraph = '';
|
|
255
|
-
return result;
|
|
256
|
-
}
|
|
257
|
-
return '';
|
|
258
|
-
};
|
|
259
|
-
for (let line of lines) {
|
|
260
|
-
const openMatch = openRegex.test(line), closeMatch = closeRegex.test(line);
|
|
261
|
-
if (openMatch || closeMatch) {
|
|
262
|
-
const blockquote = /<(\/?)blockquote[\s>](?!.*<\/?blockquote[\s>])/iu.exec(line)?.[1];
|
|
263
|
-
inBlockquote = blockquote === undefined ? inBlockquote : !blockquote;
|
|
264
|
-
pendingPTag = false;
|
|
265
|
-
output += closeParagraph();
|
|
266
|
-
inBlockElem = !closeMatch;
|
|
267
|
-
}
|
|
268
|
-
else if (!inBlockElem) {
|
|
269
|
-
if (line.startsWith(' ') && (lastParagraph === 'pre' || line.trim()) && !inBlockquote) {
|
|
270
|
-
if (lastParagraph !== 'pre') {
|
|
271
|
-
pendingPTag = false;
|
|
272
|
-
output += `${closeParagraph()}<pre>`;
|
|
273
|
-
lastParagraph = 'pre';
|
|
274
|
-
}
|
|
275
|
-
line = line.slice(1);
|
|
276
|
-
}
|
|
277
|
-
else if (/^(?:<link\b[^>]*>\s*)+$/iu.test(line)) {
|
|
278
|
-
if (pendingPTag) {
|
|
279
|
-
output += closeParagraph();
|
|
280
|
-
pendingPTag = false;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
else if (!line.trim()) {
|
|
284
|
-
if (pendingPTag) {
|
|
285
|
-
output += `${pendingPTag}<br>`;
|
|
286
|
-
pendingPTag = false;
|
|
287
|
-
lastParagraph = 'p';
|
|
288
|
-
}
|
|
289
|
-
else if (lastParagraph === 'p') {
|
|
290
|
-
pendingPTag = '</p><p>';
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
output += closeParagraph();
|
|
294
|
-
pendingPTag = '<p>';
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
else if (pendingPTag) {
|
|
298
|
-
output += pendingPTag;
|
|
299
|
-
pendingPTag = false;
|
|
300
|
-
lastParagraph = 'p';
|
|
301
|
-
}
|
|
302
|
-
else if (lastParagraph !== 'p') {
|
|
303
|
-
output += `${closeParagraph()}<p>`;
|
|
304
|
-
lastParagraph = 'p';
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
if (!pendingPTag) {
|
|
308
|
-
output += `${line}\n`;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
output += closeParagraph();
|
|
312
|
-
constants_1.states.delete(expanded);
|
|
313
|
-
html = output.trimEnd();
|
|
314
|
-
}
|
|
315
|
-
else {
|
|
316
|
-
index_1.default.viewOnly = false;
|
|
317
|
-
html = this.cloneNode().toHtmlInternal();
|
|
318
|
-
}
|
|
319
|
-
index_1.default.viewOnly = viewOnly;
|
|
320
|
-
return html;
|
|
321
|
-
};
|
|
322
|
-
index_2.Token.prototype.createComment = /** @implements */ function (data = '') {
|
|
11
|
+
index_1.Token.prototype.createComment = /** @implements */ function (data = '') {
|
|
323
12
|
// @ts-expect-error abstract class
|
|
324
13
|
return debug_1.Shadow.run(() => new comment_1.CommentToken(data.replaceAll('-->', '-->'), true, this.getAttribute('config')));
|
|
325
14
|
};
|
|
326
|
-
|
|
15
|
+
index_1.Token.prototype.createElement = /** @implements */ function (tagName, { selfClosing, closing } = {}) {
|
|
327
16
|
const config = this.getAttribute('config'), include = this.getAttribute('include');
|
|
328
17
|
if (tagName === (include ? 'noinclude' : 'includeonly')) {
|
|
329
18
|
return debug_1.Shadow.run(
|
|
@@ -347,7 +36,7 @@ index_2.Token.prototype.createElement = /** @implements */ function (tagName, {
|
|
|
347
36
|
/* istanbul ignore next */
|
|
348
37
|
throw new RangeError(`Invalid tag name: ${tagName}`);
|
|
349
38
|
};
|
|
350
|
-
|
|
39
|
+
index_1.Token.prototype.sections = /** @implements */ function () {
|
|
351
40
|
if (this.type !== 'root') {
|
|
352
41
|
return undefined;
|
|
353
42
|
}
|
|
@@ -379,7 +68,7 @@ index_2.Token.prototype.sections = /** @implements */ function () {
|
|
|
379
68
|
sections.unshift(range);
|
|
380
69
|
return sections;
|
|
381
70
|
};
|
|
382
|
-
|
|
71
|
+
index_1.Token.prototype.findEnclosingHtml = /** @implements */ function (tag) {
|
|
383
72
|
tag &&= tag.toLowerCase();
|
|
384
73
|
const { html } = this.getAttribute('config'), normalTags = new Set(html[0]), voidTags = new Set(html[2]);
|
|
385
74
|
/* istanbul ignore next */
|