wikiparser-node 1.21.1 → 1.21.2

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.
Files changed (68) hide show
  1. package/README.md +11 -3
  2. package/bundle/bundle-es8.min.js +23 -23
  3. package/bundle/bundle-lsp.min.js +24 -24
  4. package/bundle/bundle.min.js +23 -23
  5. package/coverage/badge.svg +1 -1
  6. package/dist/addon/table.js +5 -5
  7. package/dist/addon/token.js +116 -7
  8. package/dist/bin/config.js +3 -3
  9. package/dist/index.d.ts +7 -0
  10. package/dist/lib/element.d.ts +1 -0
  11. package/dist/lib/element.js +1 -0
  12. package/dist/lib/lsp.d.ts +2 -0
  13. package/dist/lib/lsp.js +5 -4
  14. package/dist/lib/node.d.ts +20 -3
  15. package/dist/lib/node.js +39 -17
  16. package/dist/lib/text.d.ts +2 -0
  17. package/dist/lib/text.js +17 -12
  18. package/dist/lib/title.d.ts +3 -1
  19. package/dist/lib/title.js +3 -1
  20. package/dist/mixin/sol.js +1 -1
  21. package/dist/parser/list.js +6 -3
  22. package/dist/parser/table.js +2 -2
  23. package/dist/src/arg.js +1 -1
  24. package/dist/src/attribute.d.ts +1 -0
  25. package/dist/src/attribute.js +6 -3
  26. package/dist/src/attributes.js +1 -1
  27. package/dist/src/converter.js +1 -1
  28. package/dist/src/converterFlags.js +1 -1
  29. package/dist/src/extLink.js +2 -2
  30. package/dist/src/gallery.d.ts +12 -2
  31. package/dist/src/gallery.js +12 -2
  32. package/dist/src/heading.d.ts +6 -1
  33. package/dist/src/heading.js +7 -2
  34. package/dist/src/hidden.js +1 -1
  35. package/dist/src/html.js +1 -1
  36. package/dist/src/imageParameter.d.ts +1 -0
  37. package/dist/src/imageParameter.js +3 -2
  38. package/dist/src/imagemap.js +4 -4
  39. package/dist/src/index.d.ts +3 -0
  40. package/dist/src/index.js +14 -6
  41. package/dist/src/link/base.js +4 -4
  42. package/dist/src/link/file.d.ts +9 -1
  43. package/dist/src/link/file.js +13 -5
  44. package/dist/src/link/galleryImage.js +3 -1
  45. package/dist/src/link/redirectTarget.d.ts +6 -1
  46. package/dist/src/link/redirectTarget.js +7 -2
  47. package/dist/src/magicLink.d.ts +6 -1
  48. package/dist/src/magicLink.js +7 -2
  49. package/dist/src/nested.js +1 -1
  50. package/dist/src/nowiki/comment.js +1 -1
  51. package/dist/src/nowiki/index.js +1 -1
  52. package/dist/src/nowiki/listBase.d.ts +6 -1
  53. package/dist/src/nowiki/listBase.js +7 -2
  54. package/dist/src/nowiki/quote.d.ts +6 -1
  55. package/dist/src/nowiki/quote.js +9 -2
  56. package/dist/src/parameter.js +1 -1
  57. package/dist/src/redirect.js +1 -1
  58. package/dist/src/table/index.js +8 -7
  59. package/dist/src/table/td.js +1 -1
  60. package/dist/src/table/trBase.js +2 -2
  61. package/dist/src/transclude.d.ts +21 -8
  62. package/dist/src/transclude.js +43 -26
  63. package/dist/util/html.js +3 -6
  64. package/dist/util/lint.js +6 -3
  65. package/extensions/dist/base.js +1 -1
  66. package/i18n/zh-hans.json +1 -1
  67. package/i18n/zh-hant.json +1 -1
  68. package/package.json +2 -2
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="98" height="20" role="img" aria-label="Coverage: 86%"><title>Coverage: 86%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="98" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="35" height="20" fill="#4c1"/><rect width="98" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="795" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="250">86%</text><text x="795" y="140" transform="scale(.1)" fill="#fff" textLength="250">86%</text></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="98" height="20" role="img" aria-label="Coverage: 87%"><title>Coverage: 87%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="98" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="35" height="20" fill="#4c1"/><rect width="98" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="795" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="250">87%</text><text x="795" y="140" transform="scale(.1)" fill="#fff" textLength="250">87%</text></g></svg>
@@ -211,7 +211,7 @@ index_2.TableToken.prototype.insertTableRow =
211
211
  // @ts-expect-error abstract class
212
212
  const token = debug_1.Shadow.run(() => new tr_1.TrToken('\n|-', undefined, this.getAttribute('config')));
213
213
  token.setAttr(attr);
214
- if (reference?.type === 'table') { // `row === 0`且表格自身是有效行
214
+ if (reference?.is('table')) { // `row === 0`且表格自身是有效行
215
215
  reference = this.prependTableRow();
216
216
  }
217
217
  this.insertBefore(token, reference);
@@ -289,7 +289,7 @@ index_2.TableToken.prototype.removeTableRow =
289
289
  }
290
290
  }
291
291
  }
292
- const row = rows[y], rowToken = row.type === 'tr' ? row : this.prependTableRow();
292
+ const row = rows[y], rowToken = row.is('tr') ? row : this.prependTableRow();
293
293
  rowToken.remove();
294
294
  return rowToken;
295
295
  };
@@ -387,7 +387,7 @@ index_2.TableToken.prototype.replicateTableRow =
387
387
  /** @implements */
388
388
  function (row) {
389
389
  let rowToken = this.getNthRow(row);
390
- if (rowToken.type === 'table') {
390
+ if (rowToken.is('table')) {
391
391
  rowToken = this.prependTableRow();
392
392
  }
393
393
  const replicated = this.insertBefore(rowToken.cloneNode(), rowToken);
@@ -438,7 +438,7 @@ index_2.TableToken.prototype.moveTableRowBefore =
438
438
  }
439
439
  }
440
440
  let beforeToken = this.getNthRow(before);
441
- if (beforeToken.type === 'table') {
441
+ if (beforeToken.is('table')) {
442
442
  beforeToken = this.prependTableRow();
443
443
  }
444
444
  this.insertBefore(rowToken, beforeToken);
@@ -447,7 +447,7 @@ index_2.TableToken.prototype.moveTableRowBefore =
447
447
  index_2.TableToken.prototype.moveTableRowAfter =
448
448
  /** @implements */
449
449
  function (y, after) {
450
- const layout = this.getLayout(), afterToken = this.getNthRow(after), cells = afterToken.childNodes.filter(child => child.type === 'td' && child.subtype !== 'caption');
450
+ const layout = this.getLayout(), afterToken = this.getNthRow(after), cells = afterToken.childNodes.filter(child => child.is('td') && child.subtype !== 'caption');
451
451
  try {
452
452
  strict_1.default.deepEqual(occupied(layout, y), occupied(layout, after, true, cells));
453
453
  }
@@ -15,7 +15,41 @@ const include_1 = require("../src/tagPair/include");
15
15
  const ext_1 = require("../src/tagPair/ext");
16
16
  const html_1 = require("../src/html");
17
17
  const attributes_1 = require("../src/attributes");
18
- const blockElems = 'table|h1|h2|h3|h4|h5|h6|pre|p|ul|ol|dl', antiBlockElems = 'td|th';
18
+ const blockElems = 'table|h1|h2|h3|h4|h5|h6|pre|p|ul|ol|dl', antiBlockElems = 'td|th', solvedMagicWords = new Set([
19
+ 'if',
20
+ 'ifeq',
21
+ 'ifexist',
22
+ 'switch',
23
+ ]), expandedMagicWords = new Set([
24
+ 'currentmonth',
25
+ 'currentmonth1',
26
+ 'currentmonthname',
27
+ 'currentmonthnamegen',
28
+ 'currentmonthabbrev',
29
+ 'currentday',
30
+ 'currentday2',
31
+ 'currentdayname',
32
+ 'currentyear',
33
+ 'currenttime',
34
+ 'currenthour',
35
+ 'currentweek',
36
+ 'currentdow',
37
+ 'currenttimestamp',
38
+ 'localmonth',
39
+ 'localmonth1',
40
+ 'localmonthname',
41
+ 'localmonthnamegen',
42
+ 'localmonthabbrev',
43
+ 'localday',
44
+ 'localday2',
45
+ 'localdayname',
46
+ 'localyear',
47
+ 'localtime',
48
+ 'localhour',
49
+ 'localweek',
50
+ 'localdow',
51
+ 'localtimestamp',
52
+ ]);
19
53
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
20
54
  /<(?:table|\/(?:td|th)|\/?(?:tr|caption|dt|dd|li))\b/iu;
21
55
  const openRegex = new RegExp(String.raw `<(?:${blockElems}|\/(?:${antiBlockElems})|\/?(?:tr|caption|dt|dd|li))\b`, 'iu');
@@ -54,11 +88,12 @@ const parseIf = (accum, prev, effective) => {
54
88
  * @param config
55
89
  * @param include
56
90
  * @param context 模板调用环境
91
+ * @param now 当前时间
57
92
  * @param accum
58
93
  * @param stack 模板调用栈
59
94
  */
60
- const expand = (wikitext, config, include, context, accum = [], stack = []) => {
61
- const magicWords = new Set(['if', 'ifeq', 'ifexist', 'switch']), n = accum.length, token = new index_2.Token(wikitext, config, accum);
95
+ const expand = (wikitext, config, include, context, now = index_1.default.now ?? new Date(), accum = [], stack = []) => {
96
+ const n = accum.length, token = new index_2.Token(wikitext, config, accum);
62
97
  token.type = 'root';
63
98
  token.parseOnce(0, include);
64
99
  if (context !== false) {
@@ -129,9 +164,82 @@ const expand = (wikitext, config, include, context, accum = [], stack = []) => {
129
164
  else if (stack.includes(title)) {
130
165
  return `${prev}<span class="error">Template loop detected: [[${title}]]</span>`;
131
166
  }
132
- return implicitNewLine(expand(index_1.default.templates.get(title), config, true, target, accum, [...stack, title]).toString(), prev);
167
+ return implicitNewLine(expand(index_1.default.templates.get(title), config, true, target, now, accum, [...stack, title]).toString(), prev);
133
168
  }
134
- else if (!magicWords.has(name)) {
169
+ else if (expandedMagicWords.has(name)) {
170
+ if (context === false) {
171
+ return m;
172
+ }
173
+ switch (name) {
174
+ case 'currentyear':
175
+ return `${prev}${now.getUTCFullYear()}`;
176
+ case 'currentmonth':
177
+ return prev + String(now.getUTCMonth() + 1).padStart(2, '0');
178
+ case 'currentmonth1':
179
+ return `${prev}${now.getUTCMonth() + 1}`;
180
+ case 'currentmonthname':
181
+ case 'currentmonthnamegen':
182
+ return prev + now.toLocaleString('default', { month: 'long', timeZone: 'UTC' });
183
+ case 'currentmonthabbrev':
184
+ return prev + now.toLocaleString('default', { month: 'short', timeZone: 'UTC' });
185
+ case 'currentday':
186
+ return `${prev}${now.getUTCDate()}`;
187
+ case 'currentday2':
188
+ return prev + String(now.getUTCDate()).padStart(2, '0');
189
+ case 'currentdow':
190
+ return `${prev}${now.getUTCDay()}`;
191
+ case 'currentdayname':
192
+ return prev + now.toLocaleString('default', { weekday: 'long', timeZone: 'UTC' });
193
+ case 'currenttime':
194
+ return `${prev}${String(now.getUTCHours()).padStart(2, '0')}:${String(now.getUTCMinutes()).padStart(2, '0')}`;
195
+ case 'currenthour':
196
+ return prev + String(now.getUTCHours()).padStart(2, '0');
197
+ case 'currentweek': {
198
+ const firstDay = new Date(Date.UTC(now.getUTCFullYear(), 0, 1));
199
+ return `${prev}${Math.ceil((now.getTime() - firstDay.getTime()) / 1e3 / 60 / 60 / 24 / 7)}`;
200
+ }
201
+ case 'currenttimestamp':
202
+ return prev + now.toISOString().slice(0, 19)
203
+ .replace(/[-:T]/gu, '');
204
+ case 'localyear':
205
+ return `${prev}${now.getFullYear()}`;
206
+ case 'localmonth':
207
+ return prev + String(now.getMonth() + 1).padStart(2, '0');
208
+ case 'localmonth1':
209
+ return `${prev}${now.getMonth() + 1}`;
210
+ case 'localmonthname':
211
+ case 'localmonthnamegen':
212
+ return prev + now.toLocaleString('default', { month: 'long' });
213
+ case 'localmonthabbrev':
214
+ return prev + now.toLocaleString('default', { month: 'short' });
215
+ case 'localday':
216
+ return `${prev}${now.getDate()}`;
217
+ case 'localday2':
218
+ return prev + String(now.getDate()).padStart(2, '0');
219
+ case 'localdow':
220
+ return `${prev}${now.getDay()}`;
221
+ case 'localdayname':
222
+ return prev + now.toLocaleString('default', { weekday: 'long' });
223
+ case 'localtime':
224
+ return `${prev}${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
225
+ case 'localhour':
226
+ return prev + String(now.getHours()).padStart(2, '0');
227
+ case 'localweek': {
228
+ const firstDay = new Date(now.getFullYear(), 0, 1);
229
+ return `${prev}${Math.ceil((now.getTime() - firstDay.getTime()) / 1e3 / 60 / 60 / 24 / 7)}`;
230
+ }
231
+ case 'localtimestamp':
232
+ return prev
233
+ + String(now.getFullYear())
234
+ + String(now.getMonth() + 1).padStart(2, '0')
235
+ + String(now.getDate()).padStart(2, '0')
236
+ + String(now.getHours()).padStart(2, '0')
237
+ + String(now.getMinutes()).padStart(2, '0')
238
+ + String(now.getSeconds()).padStart(2, '0');
239
+ // no default
240
+ }
241
+ }
242
+ else if (!solvedMagicWords.has(name)) {
135
243
  return m;
136
244
  }
137
245
  else if (length < 3 || name === 'ifeq' && length === 3) {
@@ -308,8 +416,9 @@ index_2.Token.prototype.createElement = /** @implements */ function (tagName, {
308
416
  () => new include_1.IncludeToken(tagName, '', undefined, selfClosing ? undefined : tagName, config));
309
417
  }
310
418
  else if (config.ext.includes(tagName)) {
419
+ return debug_1.Shadow.run(
311
420
  // @ts-expect-error abstract class
312
- return debug_1.Shadow.run(() => new ext_1.ExtToken(tagName, '', undefined, selfClosing ? undefined : '', config, include));
421
+ () => new ext_1.ExtToken(tagName, '', undefined, selfClosing ? undefined : '', config, include));
313
422
  }
314
423
  else if (config.html.some(tags => tags.includes(tagName))) {
315
424
  return debug_1.Shadow.run(() => {
@@ -328,7 +437,7 @@ index_2.Token.prototype.sections = /** @implements */ function () {
328
437
  return undefined;
329
438
  }
330
439
  const { childNodes, length } = this, headings = [...childNodes.entries()]
331
- .filter((entry) => entry[1].type === 'heading')
440
+ .filter((entry) => entry[1].is('heading'))
332
441
  .map(([i, { level }]) => [i, level]), lastHeading = [-1, -1, -1, -1, -1, -1], sections = headings.map(([i]) => {
333
442
  const range = this.createRange();
334
443
  range.setStart(this, i);
@@ -6,7 +6,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const fs_1 = __importDefault(require("fs"));
9
- const assert_1 = __importDefault(require("assert"));
9
+ const strict_1 = __importDefault(require("assert/strict"));
10
10
  const cm_1 = require("@bhsd/common/dist/cm");
11
11
  const diff_1 = require("../util/diff");
12
12
  /**
@@ -145,10 +145,10 @@ exports.default = async (site, url, force, internal) => {
145
145
  const oldConfig = arrToObj(require(file)), newConfig = arrToObj(config);
146
146
  for (const [k, v] of Object.entries(newConfig)) {
147
147
  try {
148
- assert_1.default.deepStrictEqual(oldConfig[k], v);
148
+ strict_1.default.deepStrictEqual(oldConfig[k], v);
149
149
  }
150
150
  catch (e) {
151
- if (e instanceof assert_1.default.AssertionError) {
151
+ if (e instanceof strict_1.default.AssertionError) {
152
152
  (0, diff_1.error)(`Configuration mismatch for "${k}"`);
153
153
  delete e.actual;
154
154
  delete e.expected;
package/dist/index.d.ts CHANGED
@@ -13,6 +13,13 @@ declare interface Parser extends ParserBase {
13
13
  templates: Map<string, string>;
14
14
  warning: boolean;
15
15
  debugging: boolean;
16
+ /**
17
+ * Specify the current time of the parser
18
+ *
19
+ * 指定解析器的当前时间
20
+ * @since v1.21.2
21
+ */
22
+ now?: Date;
16
23
  /**
17
24
  * Normalize page title
18
25
  *
@@ -224,6 +224,7 @@ export declare abstract class AstElement extends AstNode {
224
224
  * Escape `=` and `|`
225
225
  *
226
226
  * 转义 `=` 和 `|`
227
+ * @since v1.18.3
227
228
  */
228
229
  escape(): void;
229
230
  }
@@ -582,6 +582,7 @@ let AstElement = (() => {
582
582
  * Escape `=` and `|`
583
583
  *
584
584
  * 转义 `=` 和 `|`
585
+ * @since v1.18.3
585
586
  */
586
587
  escape() {
587
588
  for (const child of this.childNodes) {
package/dist/lib/lsp.d.ts CHANGED
@@ -127,6 +127,7 @@ export declare class LanguageService implements LanguageServiceBase {
127
127
  *
128
128
  * 提供 CodeLens
129
129
  * @param text source Wikitext / 源代码
130
+ * @since v1.16.3
130
131
  */
131
132
  provideInlayHints(text: string): Promise<InlayHint[]>;
132
133
  /**
@@ -149,6 +150,7 @@ export declare class LanguageService implements LanguageServiceBase {
149
150
  * 设置目标维基百科
150
151
  * @param wiki Wikipedia URL / 维基百科网址
151
152
  * @throws `RangeError` 不是有效的维基百科网址
153
+ * @since v1.18.1
152
154
  */
153
155
  setTargetWikipedia(wiki: string): Promise<void>;
154
156
  }
package/dist/lib/lsp.js CHANGED
@@ -63,7 +63,7 @@ exports.isAttr = isAttr;
63
63
  * Check if a token is an HTML attribute.
64
64
  * @param token
65
65
  */
66
- const isHtmlAttr = (token) => token.type === 'html-attr' || token.type === 'table-attr';
66
+ const isHtmlAttr = (token) => token.is('html-attr') || token.is('table-attr');
67
67
  /**
68
68
  * Check if all child nodes are plain text or comments.
69
69
  * @param token
@@ -651,7 +651,7 @@ class LanguageService {
651
651
  if (t === 'magic-word' && n !== 'invoke') {
652
652
  return undefined;
653
653
  }
654
- const key = this.#text.slice(cur.getAbsoluteIndex(), root.indexFromPos(line, character)).trimStart(), [mod, func] = t === 'magic-word' ? transclusion.getModule() : [];
654
+ const key = this.#text.slice(cur.getAbsoluteIndex(), root.indexFromPos(line, character)).trimStart(), { module: mod, function: func } = transclusion;
655
655
  return key
656
656
  ? getCompletion(root.querySelectorAll('parameter').filter(token => {
657
657
  if (token === parentNode
@@ -663,8 +663,7 @@ class LanguageService {
663
663
  else if (t === 'template') {
664
664
  return true;
665
665
  }
666
- const [m, f] = token.parentNode.getModule();
667
- return m === mod && f === func;
666
+ return token.parentNode.module === mod && token.parentNode.function === func;
668
667
  }).map(({ name }) => name), 'Variable', key, position, type === 'parameter-value' ? '=' : '')
669
668
  : undefined;
670
669
  /* NOT FOR BROWSER ONLY */
@@ -1302,6 +1301,7 @@ class LanguageService {
1302
1301
  *
1303
1302
  * 提供 CodeLens
1304
1303
  * @param text source Wikitext / 源代码
1304
+ * @since v1.16.3
1305
1305
  */
1306
1306
  async provideInlayHints(text) {
1307
1307
  const root = await this.#queue(text);
@@ -1391,6 +1391,7 @@ class LanguageService {
1391
1391
  * 设置目标维基百科
1392
1392
  * @param wiki Wikipedia URL / 维基百科网址
1393
1393
  * @throws `RangeError` 不是有效的维基百科网址
1394
+ * @since v1.18.1
1394
1395
  */
1395
1396
  async setTargetWikipedia(wiki) {
1396
1397
  const mt = /^https?:\/\/([^./]+)\.wikipedia\.org/iu.exec(wiki);
@@ -72,11 +72,26 @@ export declare abstract class AstNode implements AstNodeBase {
72
72
  get style(): Position & Dimension & {
73
73
  padding: number;
74
74
  };
75
- /** font weigth and style / 字体样式 */
75
+ /**
76
+ * font weigth and style
77
+ *
78
+ * 字体样式
79
+ * @since v.1.8.0
80
+ */
76
81
  get font(): Font;
77
- /** whether to be bold / 是否粗体 */
82
+ /**
83
+ * whether to be bold
84
+ *
85
+ * 是否粗体
86
+ * @since v.1.8.0
87
+ */
78
88
  get bold(): boolean;
79
- /** whether to be italic / 是否斜体 */
89
+ /**
90
+ * whether to be italic
91
+ *
92
+ * 是否斜体
93
+ * @since v.1.8.0
94
+ */
80
95
  get italic(): boolean;
81
96
  constructor();
82
97
  /**
@@ -124,12 +139,14 @@ export declare abstract class AstNode implements AstNodeBase {
124
139
  *
125
140
  * 是否是某种类型的节点
126
141
  * @param type token type / 节点类型
142
+ * @since v1.10.0
127
143
  */
128
144
  is<T extends Token>(type: TokenTypes): this is T;
129
145
  /**
130
146
  * Get the text and the start/end positions of all lines
131
147
  *
132
148
  * 获取所有行的wikitext和起止位置
149
+ * @since v1.16.3
133
150
  */
134
151
  getLines(): [string, number, number][];
135
152
  /**
package/dist/lib/node.js CHANGED
@@ -124,22 +124,37 @@ class AstNode {
124
124
  get fixed() {
125
125
  return false;
126
126
  }
127
- /** font weigth and style / 字体样式 */
127
+ /**
128
+ * font weigth and style
129
+ *
130
+ * 字体样式
131
+ * @since v.1.8.0
132
+ */
128
133
  get font() {
129
134
  const { bold, italic, b = 0, i = 0 } = this.#getFont();
130
135
  return { bold: bold && b >= 0, italic: italic && i >= 0 };
131
136
  }
132
- /** whether to be bold / 是否粗体 */
137
+ /**
138
+ * whether to be bold
139
+ *
140
+ * 是否粗体
141
+ * @since v.1.8.0
142
+ */
133
143
  get bold() {
134
144
  return this.font.bold;
135
145
  }
136
- /** whether to be italic / 是否斜体 */
146
+ /**
147
+ * whether to be italic
148
+ *
149
+ * 是否斜体
150
+ * @since v.1.8.0
151
+ */
137
152
  get italic() {
138
153
  return this.font.italic;
139
154
  }
140
155
  constructor() {
141
- Object.defineProperty(this, 'childNodes', { writable: false });
142
156
  if (!index_1.default.viewOnly) {
157
+ Object.defineProperty(this, 'childNodes', { writable: false });
143
158
  Object.freeze(this.childNodes);
144
159
  }
145
160
  }
@@ -177,16 +192,18 @@ class AstNode {
177
192
  default:
178
193
  /* NOT FOR BROWSER */
179
194
  if (Object.hasOwn(this, key)) {
180
- const descriptor = Object.getOwnPropertyDescriptor(this, key);
181
- if (this.#optional.has(key)) {
182
- descriptor.enumerable = Boolean(value);
195
+ const descriptor = Object.getOwnPropertyDescriptor(this, key), bool = Boolean(value), optional = this.#optional.has(key) && descriptor.enumerable !== bool;
196
+ if (optional) {
197
+ descriptor.enumerable = bool;
183
198
  }
184
- const oldValue = this[key], frozen = typeof oldValue === 'object' && Object.isFrozen(oldValue);
185
- Object.defineProperty(this, key, { ...descriptor, value });
186
- if (frozen && typeof value === 'object') {
199
+ const oldValue = this[key];
200
+ if (typeof value === 'object' && typeof oldValue === 'object' && Object.isFrozen(oldValue)) {
187
201
  Object.freeze(value);
188
202
  }
189
- return;
203
+ if (optional || !descriptor.writable) {
204
+ Object.defineProperty(this, key, { ...descriptor, value });
205
+ return;
206
+ }
190
207
  }
191
208
  /* NOT FOR BROWSER END */
192
209
  this[key] = value; // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -309,6 +326,7 @@ class AstNode {
309
326
  *
310
327
  * 是否是某种类型的节点
311
328
  * @param type token type / 节点类型
329
+ * @since v1.10.0
312
330
  */
313
331
  is(type) {
314
332
  return this.type === type;
@@ -317,6 +335,7 @@ class AstNode {
317
335
  * Get the text and the start/end positions of all lines
318
336
  *
319
337
  * 获取所有行的wikitext和起止位置
338
+ * @since v1.16.3
320
339
  */
321
340
  getLines() {
322
341
  return (0, lint_1.cache)(this.#lines, () => {
@@ -340,12 +359,15 @@ class AstNode {
340
359
  this.#optional.add(key);
341
360
  }
342
361
  /* NOT FOR BROWSER END */
343
- Object.defineProperty(this, key, {
344
- enumerable: !permanent && Boolean(this[key]),
345
- configurable: true,
346
- /* NOT FOR BROWSER */
347
- writable: false,
348
- });
362
+ const enumerable = !permanent && Boolean(this[key]);
363
+ if (!enumerable || !index_1.default.viewOnly) {
364
+ Object.defineProperty(this, key, {
365
+ enumerable,
366
+ configurable: true,
367
+ /* NOT FOR BROWSER */
368
+ writable: index_1.default.viewOnly,
369
+ });
370
+ }
349
371
  }
350
372
  /* PRINT ONLY END */
351
373
  /* NOT FOR BROWSER */
@@ -72,6 +72,7 @@ export declare class AstText extends AstNode {
72
72
  * Escape `=` and `|`
73
73
  *
74
74
  * 转义 `=` 和 `|`
75
+ * @since v1.1.4
75
76
  */
76
77
  escape(): void;
77
78
  /**
@@ -79,6 +80,7 @@ export declare class AstText extends AstNode {
79
80
  *
80
81
  * 生成HTML
81
82
  * @param nowrap whether to disable line-wrapping / 是否不换行
83
+ * @since v1.10.0
82
84
  */
83
85
  toHtml(nowrap?: boolean): string;
84
86
  }
package/dist/lib/text.js CHANGED
@@ -176,14 +176,16 @@ let AstText = (() => {
176
176
  /** @param text 包含文本 */
177
177
  constructor(text) {
178
178
  super();
179
- /* NOT FOR BROWSER */
180
- Object.defineProperties(this, {
181
- data: {
182
- value: text,
183
- writable: false,
184
- },
185
- childNodes: { enumerable: false, configurable: false },
186
- });
179
+ if (index_1.default.viewOnly) {
180
+ this.data = text;
181
+ /* NOT FOR BROWSER */
182
+ }
183
+ else {
184
+ Object.defineProperties(this, {
185
+ data: { value: text, writable: false },
186
+ childNodes: { enumerable: false, configurable: false },
187
+ });
188
+ }
187
189
  }
188
190
  /** @private */
189
191
  toString(skip) {
@@ -453,6 +455,7 @@ let AstText = (() => {
453
455
  * Escape `=` and `|`
454
456
  *
455
457
  * 转义 `=` 和 `|`
458
+ * @since v1.1.4
456
459
  */
457
460
  escape() {
458
461
  const { TranscludeToken } = require('../src/transclude');
@@ -479,6 +482,7 @@ let AstText = (() => {
479
482
  *
480
483
  * 生成HTML
481
484
  * @param nowrap whether to disable line-wrapping / 是否不换行
485
+ * @since v1.10.0
482
486
  */
483
487
  toHtml(nowrap) {
484
488
  const { data } = this;
@@ -489,8 +493,9 @@ let AstText = (() => {
489
493
  if (/\s$/u.test(this.data)) {
490
494
  const spaces = [], mt = /\n[^\S\n]*$/u.exec(this.data);
491
495
  let { nextSibling } = this, mt2 = null;
492
- while (nextSibling
493
- && (nextSibling.type === 'comment' || nextSibling.type === 'category' || nextSibling.type === 'text')) {
496
+ while (nextSibling && (nextSibling.is('comment')
497
+ || nextSibling.is('category')
498
+ || nextSibling.type === 'text')) {
494
499
  if (nextSibling.type === 'text') {
495
500
  mt2 = mt && /^[^\S\n]*(?=\n)/u.exec(nextSibling.data);
496
501
  if (mt2 || nextSibling.data.trim()) {
@@ -500,7 +505,7 @@ let AstText = (() => {
500
505
  spaces.push(nextSibling);
501
506
  }
502
507
  }
503
- else if (mt && nextSibling.type === 'category') {
508
+ else if (mt && nextSibling.is('category')) {
504
509
  const trimmed = this.data.trimEnd();
505
510
  if (this.data !== trimmed) {
506
511
  const { length } = trimmed;
@@ -513,7 +518,7 @@ let AstText = (() => {
513
518
  }
514
519
  ({ nextSibling } = nextSibling);
515
520
  }
516
- if (mt2 || nextSibling?.type === 'table') {
521
+ if (mt2 || nextSibling?.is('table')) {
517
522
  if (mt) {
518
523
  this.deleteData(mt.index + (mt2 ? 0 : 1));
519
524
  if (mt2) {
@@ -26,7 +26,9 @@ export declare class Title {
26
26
  /** full title / 完整标题 */
27
27
  get title(): string;
28
28
  /**
29
- * file extension / 扩展名
29
+ * file extension
30
+ *
31
+ * 扩展名
30
32
  * @since v1.1.0
31
33
  */
32
34
  get extension(): string | undefined;
package/dist/lib/title.js CHANGED
@@ -56,7 +56,9 @@ class Title {
56
56
  return this.getRedirection()[1];
57
57
  }
58
58
  /**
59
- * file extension / 扩展名
59
+ * file extension
60
+ *
61
+ * 扩展名
60
62
  * @since v1.1.0
61
63
  */
62
64
  get extension() {
package/dist/mixin/sol.js CHANGED
@@ -20,7 +20,7 @@ const sol = (self) => (constructor) => {
20
20
  : '\n';
21
21
  }
22
22
  return parentNode?.type === 'root'
23
- || type === 'list' && parentNode?.type === 'list-range'
23
+ || type === 'list' && parentNode?.is('list-range')
24
24
  || type !== 'heading' && parentNode?.type === 'ext-inner' && parentNode.name === 'poem'
25
25
  ? ''
26
26
  : '\n';
@@ -16,17 +16,20 @@ const constants_1 = require("../util/constants");
16
16
  * @param accum
17
17
  */
18
18
  const parseList = (wikitext, state, config, accum) => {
19
- const mt = /^((?:\0\d+[cno]\x7F)*)([;:*#]+\s*)/u.exec(wikitext);
19
+ const mt = /^((?:\0\d+[cno]\x7F)*)([;:*#]+)(\s*)/u.exec(wikitext);
20
20
  if (!mt) {
21
21
  state.lastPrefix = '';
22
22
  return wikitext;
23
23
  }
24
- const [total, comment, prefix] = mt, prefix2 = prefix.replace(/;/gu, ':'), commonPrefixLength = (0, html_1.getCommon)(prefix2, state.lastPrefix), parts = (commonPrefixLength > 1 ? prefix.slice(commonPrefixLength - 1) : prefix).split(/(?=;)/u), isDt = parts[0].startsWith(';');
24
+ const [total, comment, prefix, space] = mt, prefix2 = prefix.replace(/;/gu, ':'), commonPrefixLength = (0, html_1.getCommon)(prefix2, state.lastPrefix), parts = ((commonPrefixLength > 1 ? prefix.slice(commonPrefixLength - 1) : prefix) + space)
25
+ .split(/(?=;)/u), isDt = parts[0].startsWith(';');
25
26
  let dt = parts.length - (isDt ? 0 : 1);
26
27
  if (commonPrefixLength > 1) {
27
28
  const commonPrefix = prefix.slice(0, commonPrefixLength - 1);
28
29
  if (isDt) {
29
- parts.unshift(commonPrefix);
30
+ const commonPrefixes = commonPrefix.split(/(?=;)/u);
31
+ parts.unshift(...commonPrefixes);
32
+ dt += commonPrefix.includes(';') ? commonPrefixes.length : 0;
30
33
  }
31
34
  else {
32
35
  parts[0] = commonPrefix + parts[0];
@@ -19,7 +19,7 @@ const isTr = (token) => token.lastChild.constructor !== index_1.Token;
19
19
  * @param top 当前解析的表格或表格行
20
20
  * @param stack 表格栈
21
21
  */
22
- const pop = (top, stack) => top.type === 'td' ? stack.pop() : top;
22
+ const pop = (top, stack) => top.is('td') ? stack.pop() : top;
23
23
  /**
24
24
  * 解析表格,注意`tr`和`td`包含开头的换行
25
25
  * @param {Token & {firstChild: AstText}} root 根节点
@@ -92,7 +92,7 @@ const parseTable = ({ firstChild: { data }, type, name }, config, accum) => {
92
92
  }
93
93
  else if (row) {
94
94
  top = pop(top, stack);
95
- if (top.type === 'tr') {
95
+ if (top.is('tr')) {
96
96
  top = stack.pop();
97
97
  }
98
98
  // @ts-expect-error abstract class