wikiparser-node 1.10.0 → 1.11.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.
Files changed (64) hide show
  1. package/dist/addon/token.js +15 -35
  2. package/dist/addon/transclude.js +0 -1
  3. package/dist/base.d.ts +1 -1
  4. package/dist/index.js +22 -18
  5. package/dist/lib/element.js +3 -2
  6. package/dist/lib/text.js +46 -7
  7. package/dist/lib/title.js +16 -13
  8. package/dist/mixin/flagsParent.d.ts +2 -0
  9. package/dist/mixin/flagsParent.js +4 -0
  10. package/dist/mixin/hidden.js +2 -2
  11. package/dist/mixin/singleLine.js +2 -2
  12. package/dist/mixin/sol.js +1 -0
  13. package/dist/parser/list.js +19 -4
  14. package/dist/parser/magicLinks.js +1 -2
  15. package/dist/parser/selector.js +1 -1
  16. package/dist/src/arg.js +5 -5
  17. package/dist/src/attribute.js +6 -9
  18. package/dist/src/attributes.js +6 -7
  19. package/dist/src/converter.js +5 -4
  20. package/dist/src/converterFlags.js +6 -9
  21. package/dist/src/converterRule.js +4 -4
  22. package/dist/src/extLink.js +22 -2
  23. package/dist/src/gallery.js +5 -4
  24. package/dist/src/heading.js +4 -3
  25. package/dist/src/html.d.ts +4 -0
  26. package/dist/src/html.js +6 -6
  27. package/dist/src/imageParameter.d.ts +2 -0
  28. package/dist/src/imageParameter.js +24 -11
  29. package/dist/src/index.d.ts +2 -7
  30. package/dist/src/index.js +97 -58
  31. package/dist/src/link/base.d.ts +1 -3
  32. package/dist/src/link/base.js +13 -20
  33. package/dist/src/link/file.d.ts +13 -7
  34. package/dist/src/link/file.js +76 -19
  35. package/dist/src/link/index.js +1 -2
  36. package/dist/src/link/redirectTarget.js +1 -4
  37. package/dist/src/magicLink.d.ts +1 -4
  38. package/dist/src/magicLink.js +12 -17
  39. package/dist/src/nested.js +1 -1
  40. package/dist/src/nowiki/base.d.ts +1 -3
  41. package/dist/src/nowiki/base.js +2 -2
  42. package/dist/src/nowiki/comment.js +1 -4
  43. package/dist/src/nowiki/dd.js +2 -2
  44. package/dist/src/nowiki/hr.js +1 -1
  45. package/dist/src/nowiki/index.js +1 -4
  46. package/dist/src/nowiki/list.js +1 -1
  47. package/dist/src/nowiki/listBase.d.ts +9 -3
  48. package/dist/src/nowiki/listBase.js +49 -25
  49. package/dist/src/nowiki/quote.js +6 -1
  50. package/dist/src/paramTag/index.d.ts +1 -3
  51. package/dist/src/paramTag/index.js +1 -1
  52. package/dist/src/parameter.js +2 -6
  53. package/dist/src/redirect.js +2 -2
  54. package/dist/src/table/base.js +2 -2
  55. package/dist/src/table/index.js +3 -2
  56. package/dist/src/table/td.js +3 -3
  57. package/dist/src/table/trBase.js +4 -4
  58. package/dist/src/tagPair/ext.js +7 -7
  59. package/dist/src/transclude.js +4 -4
  60. package/dist/util/html.js +143 -0
  61. package/dist/util/string.js +26 -23
  62. package/i18n/zh-hans.json +1 -1
  63. package/i18n/zh-hant.json +1 -1
  64. package/package.json +12 -12
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.expand = void 0;
4
3
  const fs = require("fs");
5
4
  const path = require("path");
6
5
  const constants_1 = require("../util/constants");
@@ -16,7 +15,7 @@ const attributes_1 = require("../src/attributes");
16
15
  index_2.Token.prototype.createComment = /** @implements */ function (data = '') {
17
16
  const config = this.getAttribute('config');
18
17
  // @ts-expect-error abstract class
19
- return debug_1.Shadow.run(() => new comment_1.CommentToken(data.replace(/-->/gu, '-->'), true, config));
18
+ return debug_1.Shadow.run(() => new comment_1.CommentToken(data.replaceAll('-->', '-->'), true, config));
20
19
  };
21
20
  index_2.Token.prototype.createElement = /** @implements */ function (tagName, { selfClosing, closing } = {}) {
22
21
  const config = this.getAttribute('config'), include = this.getAttribute('include');
@@ -149,25 +148,11 @@ index_2.Token.prototype.findEnclosingHtml = /** @implements */ function (tag) {
149
148
  range.setEnd(parentNode, j + 1);
150
149
  return range;
151
150
  };
152
- index_2.Token.prototype.redoQuotes = /** @implements */ function () {
153
- const acceptable = this.getAcceptable();
154
- if (acceptable && !('QuoteToken' in acceptable)) {
155
- return;
156
- }
157
- const accum = [];
158
- for (const child of this.childNodes) {
159
- if (child.type !== 'quote' && child.type !== 'text') {
160
- child.replaceWith(`\0${accum.length}e\x7F`);
161
- accum.push(child);
162
- }
163
- }
164
- const token = debug_1.Shadow.run(() => {
165
- const node = new index_2.Token(this.toString(), this.getAttribute('config'), accum);
166
- node.setAttribute('stage', 6);
167
- return node.parse(7);
168
- });
169
- this.replaceChildren(...token.childNodes);
170
- };
151
+ /**
152
+ * 隐式换行
153
+ * @param str 字符串
154
+ */
155
+ const implicitNewLine = (str) => /^(?:\{\||[:;#*])/u.test(str) ? `\n${str}` : str;
171
156
  /**
172
157
  * 展开模板
173
158
  * @param wikitext
@@ -229,7 +214,7 @@ const expand = (wikitext, config, include, context, accum = []) => {
229
214
  }
230
215
  index_1.default.templates.set(title, fs.readFileSync(file, 'utf8'));
231
216
  }
232
- return (0, exports.expand)(index_1.default.templates.get(title), config, true, target, accum).toString();
217
+ return implicitNewLine(expand(index_1.default.templates.get(title), config, true, target, accum).toString());
233
218
  }
234
219
  else if (!magicWords.has(name)) {
235
220
  return m;
@@ -243,7 +228,7 @@ const expand = (wikitext, config, include, context, accum = []) => {
243
228
  if (effective) {
244
229
  // @ts-expect-error sparse array
245
230
  accum[accum.indexOf(effective.lastChild)] = undefined;
246
- return effective.value;
231
+ return implicitNewLine(effective.value);
247
232
  }
248
233
  return '';
249
234
  }
@@ -252,21 +237,20 @@ const expand = (wikitext, config, include, context, accum = []) => {
252
237
  if (effective) {
253
238
  // @ts-expect-error sparse array
254
239
  accum[accum.indexOf(effective.lastChild)] = undefined;
255
- return effective.value;
240
+ return implicitNewLine(effective.value);
256
241
  }
257
242
  return '';
258
243
  }
259
244
  else if (name === 'switch' && known) {
260
245
  let defaultVal = '', found = false, transclusion = false, defaultParam;
261
246
  for (let j = 2; j < length; j++) {
262
- const { anon, value, firstChild, lastChild } = c[j], option = (0, string_1.removeComment)(firstChild.toString())
263
- .replace(/^[ \t\n\0\v]+|([^ \t\n\0\v])[ \t\n\0\v]+$/gu, '$1');
247
+ const { anon, value, firstChild, lastChild } = c[j], option = (0, string_1.trimPHP)((0, string_1.removeComment)(firstChild.toString()));
264
248
  transclusion = /\0\d+t\x7F/u.test(anon ? value : option);
265
249
  if (anon) {
266
250
  if (j === length - 1) {
267
251
  // @ts-expect-error sparse array
268
252
  accum[accum.indexOf(lastChild)] = undefined;
269
- return value;
253
+ return implicitNewLine(value);
270
254
  }
271
255
  else if (transclusion) {
272
256
  break;
@@ -281,7 +265,7 @@ const expand = (wikitext, config, include, context, accum = []) => {
281
265
  else if (found || option === var1) {
282
266
  // @ts-expect-error sparse array
283
267
  accum[accum.indexOf(lastChild)] = undefined;
284
- return value;
268
+ return implicitNewLine(value);
285
269
  }
286
270
  else if (option.toLowerCase() === '#default') {
287
271
  defaultVal = value;
@@ -292,12 +276,9 @@ const expand = (wikitext, config, include, context, accum = []) => {
292
276
  // @ts-expect-error sparse array
293
277
  accum[accum.indexOf(defaultParam)] = undefined;
294
278
  }
295
- return defaultVal;
279
+ return implicitNewLine(defaultVal);
296
280
  }
297
281
  }
298
- if (transclusion) {
299
- return m;
300
- }
301
282
  }
302
283
  return m;
303
284
  });
@@ -308,17 +289,16 @@ const expand = (wikitext, config, include, context, accum = []) => {
308
289
  }
309
290
  return token;
310
291
  };
311
- exports.expand = expand;
312
292
  index_2.Token.prototype.expand = /** @implements */ function (context) {
313
293
  if (this.type !== 'root') {
314
294
  throw new Error('Only root token can be expanded!');
315
295
  }
316
- return debug_1.Shadow.run(() => (0, exports.expand)(this.toString(), this.getAttribute('config'), this.getAttribute('include'), context).parse());
296
+ return debug_1.Shadow.run(() => expand(this.toString(), this.getAttribute('config'), this.getAttribute('include'), context).parse());
317
297
  };
318
298
  index_2.Token.prototype.solveConst = /** @implements */ function () {
319
299
  if (this.type !== 'root') {
320
300
  throw new Error('Only root token can be expanded!');
321
301
  }
322
- return debug_1.Shadow.run(() => (0, exports.expand)(this.toString(), this.getAttribute('config'), this.getAttribute('include'), false).parse());
302
+ return debug_1.Shadow.run(() => expand(this.toString(), this.getAttribute('config'), this.getAttribute('include'), false).parse());
323
303
  };
324
304
  constants_1.classes['ExtendedTableToken'] = __filename;
@@ -131,7 +131,6 @@ transclude_1.TranscludeToken.prototype.fixDuplication =
131
131
  let last;
132
132
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
133
133
  /^a\d+$/u;
134
- // eslint-disable-next-line es-x/no-regexp-lookbehind-assertions
135
134
  const str = key.slice(0, -/(?<!\d)\d+$/u.exec(key)[0].length), regex = new RegExp(String.raw `^${(0, string_1.escapeRegExp)(str)}\d+$`, 'u'), series = this.getAllArgs().filter(({ name }) => regex.test(name)), ordered = series.every(({ name }, i) => {
136
135
  const j = Number(name.slice(str.length)), cmp = j <= i + 1 && (i === 0 || j >= last || name === key);
137
136
  last = j;
package/dist/base.d.ts CHANGED
@@ -15,7 +15,7 @@ export interface Config {
15
15
  readonly redirects?: [string, string][];
16
16
  readonly articlePath?: string;
17
17
  }
18
- export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | 'onlyinclude' | 'noinclude' | 'include' | 'comment' | 'ext' | 'ext-attrs' | 'ext-attr-dirty' | 'ext-attr' | 'attr-key' | 'attr-value' | 'ext-inner' | 'arg' | 'arg-name' | 'arg-default' | 'hidden' | 'magic-word' | 'magic-word-name' | 'invoke-function' | 'invoke-module' | 'template' | 'template-name' | 'parameter' | 'parameter-key' | 'parameter-value' | 'heading' | 'heading-title' | 'heading-trail' | 'html' | 'html-attrs' | 'html-attr-dirty' | 'html-attr' | 'table' | 'tr' | 'td' | 'table-syntax' | 'table-attrs' | 'table-attr-dirty' | 'table-attr' | 'table-inter' | 'td-inner' | 'hr' | 'double-underscore' | 'link' | 'link-target' | 'link-text' | 'category' | 'file' | 'gallery-image' | 'imagemap-image' | 'image-parameter' | 'quote' | 'ext-link' | 'ext-link-text' | 'ext-link-url' | 'free-ext-link' | 'magic-link' | 'list' | 'dd' | 'converter' | 'converter-flags' | 'converter-flag' | 'converter-rule' | 'converter-rule-variant' | 'converter-rule-to' | 'converter-rule-from' | 'param-line' | 'imagemap-link';
18
+ export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | 'onlyinclude' | 'noinclude' | 'include' | 'comment' | 'ext' | 'ext-attrs' | 'ext-attr-dirty' | 'ext-attr' | 'attr-key' | 'attr-value' | 'ext-inner' | 'arg' | 'arg-name' | 'arg-default' | 'hidden' | 'magic-word' | 'magic-word-name' | 'invoke-function' | 'invoke-module' | 'template' | 'template-name' | 'parameter' | 'parameter-key' | 'parameter-value' | 'heading' | 'heading-title' | 'heading-trail' | 'html' | 'html-attrs' | 'html-attr-dirty' | 'html-attr' | 'table' | 'tr' | 'td' | 'table-syntax' | 'table-attrs' | 'table-attr-dirty' | 'table-attr' | 'table-inter' | 'td-inner' | 'hr' | 'double-underscore' | 'link' | 'link-target' | 'link-text' | 'category' | 'file' | 'gallery-image' | 'imagemap-image' | 'image-parameter' | 'quote' | 'ext-link' | 'ext-link-text' | 'ext-link-url' | 'free-ext-link' | 'magic-link' | 'list' | 'dd' | 'list-range' | 'converter' | 'converter-flags' | 'converter-flag' | 'converter-rule' | 'converter-rule-variant' | 'converter-rule-to' | 'converter-rule-from' | 'param-line' | 'imagemap-link';
19
19
  export declare const rules: readonly ["bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "lonely-apos", "lonely-bracket", "lonely-http", "nested-link", "no-arg", "no-duplicate", "no-ignored", "obsolete-attr", "obsolete-tag", "parsing-order", "pipe-like", "table-layout", "tag-like", "unbalanced-header", "unclosed-comment", "unclosed-quote", "unclosed-table", "unescaped", "unknown-page", "unmatched-tag", "unterminated-url", "url-encoding", "var-anchor", "void-ext"];
20
20
  export declare namespace LintError {
21
21
  type Severity = 'error' | 'warning';
package/dist/index.js CHANGED
@@ -70,27 +70,31 @@ const Parser = {
70
70
  /** @implements */
71
71
  normalizeTitle(title, defaultNs = 0, include, config = Parser.getConfig(), halfParsed, decode = false, selfLink = false) {
72
72
  const { Title } = require('./lib/title');
73
+ let titleObj;
73
74
  if (halfParsed) {
74
- return new Title(title, defaultNs, config, decode, selfLink);
75
+ titleObj = new Title(title, defaultNs, config, decode, selfLink);
75
76
  }
76
- const { Token } = require('./src/index');
77
- return debug_1.Shadow.run(() => {
78
- const root = new Token(title, config);
79
- root.type = 'root';
80
- root.parseOnce(0, include).parseOnce();
81
- const titleObj = new Title(root.toString(), defaultNs, config, decode, selfLink);
82
- for (const key of ['main', 'fragment']) {
83
- const str = titleObj[key];
84
- if (str?.includes('\0')) {
85
- titleObj[key] = root.buildFromStr(str, constants_1.BuildMethod.Text);
77
+ else {
78
+ const { Token } = require('./src/index');
79
+ titleObj = debug_1.Shadow.run(() => {
80
+ const root = new Token(title, config);
81
+ root.type = 'root';
82
+ root.parseOnce(0, include).parseOnce();
83
+ const t = new Title(root.toString(), defaultNs, config, decode, selfLink);
84
+ for (const key of ['main', 'fragment']) {
85
+ const str = t[key];
86
+ if (str?.includes('\0')) {
87
+ t[key] = root.buildFromStr(str, constants_1.BuildMethod.Text);
88
+ }
86
89
  }
87
- }
88
- /* NOT FOR BROWSER */
89
- titleObj.conversionTable = this.conversionTable;
90
- titleObj.redirects = this.redirects;
91
- /* NOT FOR BROWSER END */
92
- return titleObj;
93
- });
90
+ return t;
91
+ });
92
+ }
93
+ /* NOT FOR BROWSER */
94
+ titleObj.conversionTable = this.conversionTable;
95
+ titleObj.redirects = this.redirects;
96
+ /* NOT FOR BROWSER END */
97
+ return titleObj;
94
98
  },
95
99
  /** @implements */
96
100
  parse(wikitext, include, maxStage = constants_1.MAX_STAGE, config = Parser.getConfig()) {
@@ -73,7 +73,7 @@ class AstElement extends node_1.AstNode {
73
73
  const { innerText } = this;
74
74
  if (typeof innerText === 'string') {
75
75
  const lines = innerText.split('\n');
76
- return lines[lines.length - 1].length;
76
+ return lines.at(-1).length;
77
77
  }
78
78
  return undefined;
79
79
  }
@@ -269,8 +269,9 @@ class AstElement extends node_1.AstNode {
269
269
  }
270
270
  /** @private */
271
271
  print(opt = {}) {
272
+ const { class: cl } = opt;
272
273
  return this.toString()
273
- ? `<span class="wpb-${opt.class ?? this.type}">${(0, string_1.print)(this.childNodes, opt)}</span>`
274
+ ? `${cl === '' ? '' : `<span class="wpb-${cl ?? this.type}">`}${(0, string_1.print)(this.childNodes, opt)}${cl === '' ? '' : '</span>'}`
274
275
  : '';
275
276
  }
276
277
  /**
package/dist/lib/text.js CHANGED
@@ -4,6 +4,7 @@ exports.AstText = void 0;
4
4
  const constants_1 = require("../util/constants");
5
5
  const debug_1 = require("../util/debug");
6
6
  const string_1 = require("../util/string");
7
+ const html_1 = require("../util/html");
7
8
  const index_1 = require("../index");
8
9
  const node_1 = require("./node");
9
10
  /* eslint-disable @typescript-eslint/no-unused-expressions */
@@ -104,8 +105,8 @@ class AstText extends node_1.AstNode {
104
105
  });
105
106
  }
106
107
  /** @private */
107
- toString() {
108
- return this.data;
108
+ toString(skip) {
109
+ return skip && !this.parentNode?.getAttribute('built') ? (0, string_1.removeComment)(this.data) : this.data;
109
110
  }
110
111
  /** @private */
111
112
  text() {
@@ -236,10 +237,7 @@ class AstText extends node_1.AstNode {
236
237
  }
237
238
  else if (char === ']' && previousType === 'free-ext-link' && severity === 'error') {
238
239
  const i = start - previousSibling.toString().length;
239
- e.fix = {
240
- range: [i, i],
241
- text: '[',
242
- };
240
+ e.fix = { range: [i, i], text: '[' };
243
241
  }
244
242
  errors.push(e);
245
243
  }
@@ -354,7 +352,48 @@ class AstText extends node_1.AstNode {
354
352
  */
355
353
  toHtml(nowrap) {
356
354
  const { data } = this;
357
- return (0, string_1.font)(this, (0, string_1.sanitize)(nowrap ? data.replace(/\n/gu, ' ') : data));
355
+ return (0, html_1.font)(this, (0, string_1.sanitize)(nowrap ? data.replaceAll('\n', ' ') : data));
356
+ }
357
+ /** @private */
358
+ toHtmlInternal(nowrap) {
359
+ const mt = /\n[^\S\n]*$/u.exec(this.data);
360
+ if (mt) {
361
+ const spaces = [];
362
+ let { nextSibling } = this, mt2 = null;
363
+ while (nextSibling
364
+ && (nextSibling.type === 'comment' || nextSibling.type === 'category' || nextSibling.type === 'text')) {
365
+ if (nextSibling.type === 'text') {
366
+ const { data } = nextSibling;
367
+ mt2 = /^[^\S\n]*(?=\n)/u.exec(data);
368
+ if (mt2 || data.trim()) {
369
+ break;
370
+ }
371
+ else {
372
+ spaces.push(nextSibling);
373
+ }
374
+ }
375
+ else if (nextSibling.type === 'category') {
376
+ const trimmed = this.data.trimEnd();
377
+ if (this.data !== trimmed) {
378
+ const { length } = trimmed;
379
+ this.deleteData(length + this.data.slice(length).indexOf('\n'), Infinity);
380
+ }
381
+ for (const space of spaces) {
382
+ space.#setData('');
383
+ }
384
+ spaces.length = 0;
385
+ }
386
+ ({ nextSibling } = nextSibling);
387
+ }
388
+ if (mt2) {
389
+ this.deleteData(mt.index, Infinity);
390
+ nextSibling.deleteData(0, mt2[0].length);
391
+ for (const space of spaces) {
392
+ space.#setData('');
393
+ }
394
+ }
395
+ }
396
+ return this.toHtml(nowrap);
358
397
  }
359
398
  }
360
399
  exports.AstText = AstText;
package/dist/lib/title.js CHANGED
@@ -4,11 +4,6 @@ exports.Title = void 0;
4
4
  const string_1 = require("../util/string");
5
5
  const constants_1 = require("../util/constants");
6
6
  const index_1 = require("../index");
7
- /**
8
- * PHP的`rawurldecode`函数的JavaScript实现
9
- * @param str 要解码的字符串
10
- */
11
- const rawurldecode = (str) => decodeURIComponent(str.replace(/%(?![\da-f]{2})/giu, '%25'));
12
7
  /** MediaWiki页面标题对象 */
13
8
  class Title {
14
9
  #main;
@@ -50,7 +45,7 @@ class Title {
50
45
  return redirected;
51
46
  }
52
47
  this.autoConvert();
53
- title = (prefix + this.main).replace(/ /gu, '_');
48
+ title = (prefix + this.main).replaceAll(' ', '_');
54
49
  redirected = this.#redirect(title);
55
50
  if (redirected) {
56
51
  return redirected;
@@ -84,12 +79,12 @@ class Title {
84
79
  if (decode && title.includes('%')) {
85
80
  try {
86
81
  const encoded = /%(?!21|3[ce]|5[bd]|7[b-d])[\da-f]{2}/iu.test(title);
87
- title = rawurldecode(title);
82
+ title = (0, string_1.rawurldecode)(title);
88
83
  this.encoded = encoded;
89
84
  }
90
85
  catch { }
91
86
  }
92
- title = title.replace(/_/gu, ' ').trim();
87
+ title = title.replace(/[_ ]+/gu, ' ').trim();
93
88
  if (subpage) {
94
89
  this.ns = 0;
95
90
  }
@@ -108,7 +103,7 @@ class Title {
108
103
  /* NOT FOR BROWSER END */
109
104
  const m = title.split(':');
110
105
  if (m.length > 1) {
111
- const id = config.nsid[m[0].trim().toLowerCase()];
106
+ const k = m[0].trim().toLowerCase(), id = Object.prototype.hasOwnProperty.call(config.nsid, k) && config.nsid[k];
112
107
  if (id) {
113
108
  ns = id;
114
109
  title = m.slice(1).join(':').trim();
@@ -121,7 +116,7 @@ class Title {
121
116
  let fragment = title.slice(i + 1).trimEnd();
122
117
  if (fragment.includes('%')) {
123
118
  try {
124
- fragment = rawurldecode(fragment);
119
+ fragment = (0, string_1.rawurldecode)(fragment);
125
120
  }
126
121
  catch { }
127
122
  }
@@ -196,9 +191,17 @@ class Title {
196
191
  }
197
192
  /** 生成URL */
198
193
  getUrl() {
199
- return this.#path.replace('$1', `${encodeURIComponent(this.title)}${this.fragment === undefined && this.#fragment === undefined
200
- ? ''
201
- : `#${encodeURIComponent(this.fragment ?? this.#fragment)}`}`);
194
+ const { title, fragment } = this;
195
+ if (title) {
196
+ return this.#path.replace('$1', `${encodeURIComponent(title)}${fragment === undefined && this.#fragment === undefined
197
+ ? ''
198
+ : `#${encodeURIComponent(fragment ?? this.#fragment)}`}`);
199
+ }
200
+ return fragment === undefined ? '' : `#${encodeURIComponent(fragment)}`;
201
+ }
202
+ /** @private */
203
+ getTitleAttr() {
204
+ return this.title.replace(/["_]/gu, p => p === '"' ? '&quot;' : ' ');
202
205
  }
203
206
  }
204
207
  exports.Title = Title;
@@ -8,6 +8,8 @@ export interface FlagsParentBase {
8
8
  getEffectiveFlags(): Set<string>;
9
9
  /** 获取未知的转换类型标记 */
10
10
  getUnknownFlags(): Set<string>;
11
+ /** 获取指定语言变体的转换标记 */
12
+ getVariantFlags(): Set<string>;
11
13
  /**
12
14
  * 是否具有某转换类型标记
13
15
  * @param flag 转换类型标记
@@ -33,6 +33,10 @@ const flagsParent = (constructor, _) => {
33
33
  return this.firstChild.getUnknownFlags();
34
34
  }
35
35
  /** @implements */
36
+ getVariantFlags() {
37
+ return this.firstChild.getVariantFlags();
38
+ }
39
+ /** @implements */
36
40
  hasFlag(flag) {
37
41
  return this.firstChild.hasFlag(flag);
38
42
  }
@@ -26,8 +26,8 @@ const hiddenToken = (linter = true, html = true) => (constructor, _) => {
26
26
  dispatchEvent() {
27
27
  //
28
28
  }
29
- toHtml(nowrap) {
30
- return html ? '' : super.toHtml(nowrap);
29
+ toHtmlInternal(nowrap) {
30
+ return html ? '' : super.toHtmlInternal(nowrap);
31
31
  }
32
32
  }
33
33
  (0, debug_1.mixin)(AnyHiddenToken, constructor);
@@ -12,10 +12,10 @@ const singleLine = (constructor, _) => {
12
12
  /** 不可包含换行符的类 */
13
13
  class SingleLineToken extends constructor {
14
14
  toString(skip) {
15
- return super.toString(skip).replace(/\n/gu, ' ');
15
+ return super.toString(skip).replaceAll('\n', ' ');
16
16
  }
17
17
  text() {
18
- return super.text().replace(/\n/gu, ' ');
18
+ return super.text().replaceAll('\n', ' ');
19
19
  }
20
20
  }
21
21
  (0, debug_1.mixin)(SingleLineToken, constructor);
package/dist/mixin/sol.js CHANGED
@@ -21,6 +21,7 @@ const sol = (self) => (constructor, _) => {
21
21
  : '\n';
22
22
  }
23
23
  return parentNode?.type === 'root'
24
+ || type === 'list' && parentNode?.type === 'list-range'
24
25
  || type !== 'heading' && parentNode?.type === 'ext-inner' && parentNode.name === 'poem'
25
26
  ? ''
26
27
  : '\n';
@@ -2,21 +2,36 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseList = void 0;
4
4
  const constants_1 = require("../util/constants");
5
+ const html_1 = require("../util/html");
5
6
  const list_1 = require("../src/nowiki/list");
6
7
  const dd_1 = require("../src/nowiki/dd");
7
8
  /**
8
9
  * 解析列表
9
10
  * @param wikitext
11
+ * @param state
12
+ * @param state.lastPrefix 上一个列表的前缀
10
13
  * @param config
11
14
  * @param accum
12
15
  */
13
- const parseList = (wikitext, config, accum) => {
16
+ const parseList = (wikitext, state, config, accum) => {
14
17
  const mt = /^((?:\0\d+c\x7F)*)([;:*#]+\s*)/u.exec(wikitext);
15
18
  if (!mt) {
19
+ state.lastPrefix = '';
16
20
  return wikitext;
17
21
  }
18
- const [total, comment, prefix] = mt, parts = prefix.split(/(?=;)/u);
19
- let text = comment + parts.map((_, i) => `\0${accum.length + i}d\x7F`).join('') + wikitext.slice(total.length), dt = parts.length - (parts[0].startsWith(';') ? 0 : 1);
22
+ 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(';');
23
+ let dt = parts.length - (isDt ? 0 : 1);
24
+ if (commonPrefixLength > 1) {
25
+ const commonPrefix = prefix.slice(0, commonPrefixLength - 1);
26
+ if (isDt) {
27
+ parts.unshift(commonPrefix);
28
+ }
29
+ else {
30
+ parts[0] = commonPrefix + parts[0];
31
+ }
32
+ }
33
+ state.lastPrefix = prefix2;
34
+ let text = comment + parts.map((_, i) => `\0${accum.length + i}d\x7F`).join('') + wikitext.slice(total.length);
20
35
  for (const part of parts) {
21
36
  // @ts-expect-error abstract class
22
37
  new list_1.ListToken(part, config, accum);
@@ -24,7 +39,7 @@ const parseList = (wikitext, config, accum) => {
24
39
  if (!dt) {
25
40
  return text;
26
41
  }
27
- const { html: [normalTags] } = config, fullRegex = /:+\s*|-\{|\0\d+[xq]\x7F/gu;
42
+ const { html: [normalTags] } = config, fullRegex = /:+|-\{|\0\d+[xq]\x7F/gu;
28
43
  let regex = fullRegex, ex = regex.exec(text), lt = 0, lb = false, li = false, lc = 0;
29
44
  /**
30
45
  * 创建`DdToken`
@@ -4,7 +4,6 @@ exports.parseMagicLinks = void 0;
4
4
  const string_1 = require("../util/string");
5
5
  const constants_1 = require("../util/constants");
6
6
  const magicLink_1 = require("../src/magicLink");
7
- const sepRegex = /[^,;\\.:!?)][,;\\.:!?)]+$/u, sepLparRegex = /[^,;\\.:!?][,;\\.:!?]+$/u;
8
7
  /**
9
8
  * 解析自由外链
10
9
  * @param wikitext
@@ -24,7 +23,7 @@ const parseMagicLinks = (wikitext, config, accum) => {
24
23
  trail = url.slice(m2.index);
25
24
  url = url.slice(0, m2.index);
26
25
  }
27
- const sep = url.includes('(') ? sepLparRegex : sepRegex, sepChars = sep.exec(url);
26
+ const sep = url.includes('(') ? /[^,;\\.:!?][,;\\.:!?]+$/u : /[^,;\\.:!?)][,;\\.:!?)]+$/u, sepChars = sep.exec(url);
28
27
  if (sepChars) {
29
28
  let correction = 1;
30
29
  if (sepChars[0][1] === ';'
@@ -239,7 +239,7 @@ const matchesArray = (token, copy) => {
239
239
  const condition = [...copy];
240
240
  if (matches(token, condition.pop())) {
241
241
  const { parentNode, previousElementSibling } = token;
242
- switch (condition[condition.length - 1]?.relation) {
242
+ switch (condition.at(-1)?.relation) {
243
243
  case undefined:
244
244
  return true;
245
245
  case '>':
package/dist/src/arg.js CHANGED
@@ -76,10 +76,7 @@ class ArgToken extends index_2.Token {
76
76
  if (!this.getAttribute('include')) {
77
77
  const e = (0, lint_1.generateForSelf)(this, { start }, 'no-arg', 'unexpected template argument');
78
78
  if (argDefault) {
79
- e.fix = {
80
- range: [start, e.endIndex],
81
- text: argDefault.text(),
82
- };
79
+ e.fix = { range: [start, e.endIndex], text: argDefault.text() };
83
80
  }
84
81
  return [e];
85
82
  }
@@ -171,7 +168,10 @@ class ArgToken extends index_2.Token {
171
168
  */
172
169
  insertAt(token, i = this.length) {
173
170
  i += i < 0 ? this.length : 0;
174
- if (i > 1) {
171
+ if (debug_1.Shadow.running) {
172
+ //
173
+ }
174
+ else if (i > 1) {
175
175
  this.constructorError('cannot insert redundant child nodes');
176
176
  }
177
177
  else if (typeof token === 'string') {
@@ -335,10 +335,7 @@ let AttributeToken = (() => {
335
335
  const e = (0, lint_1.generateForChild)(lastChild, rect, 'unclosed-quote', index_1.default.msg('unclosed $1', 'quotes'), 'warning');
336
336
  e.startIndex--;
337
337
  e.startCol--;
338
- const fix = {
339
- range: [e.endIndex, e.endIndex],
340
- text: this.#quotes[0],
341
- };
338
+ const fix = { range: [e.endIndex, e.endIndex], text: this.#quotes[0] };
342
339
  if (lastChild.childNodes.some(({ type: t, data }) => t === 'text' && /\s/u.test(data))) {
343
340
  e.suggestions = [
344
341
  {
@@ -395,10 +392,10 @@ let AttributeToken = (() => {
395
392
  }
396
393
  /* NOT FOR BROWSER */
397
394
  cloneNode() {
398
- const [key, value] = this.cloneChildNodes(), config = this.getAttribute('config');
395
+ const [key, value] = this.cloneChildNodes(), k = key.toString(), config = this.getAttribute('config');
399
396
  return debug_1.Shadow.run(() => {
400
397
  // @ts-expect-error abstract class
401
- const token = new AttributeToken(this.type, this.tag, '', this.#equal, '', this.#quotes, config);
398
+ const token = new AttributeToken(this.type, this.tag, k, this.#equal, '', this.#quotes, config);
402
399
  token.firstChild.safeReplaceWith(key);
403
400
  token.lastChild.safeReplaceWith(value);
404
401
  return token;
@@ -461,20 +458,20 @@ let AttributeToken = (() => {
461
458
  this.firstChild.replaceChildren(...childNodes);
462
459
  }
463
460
  /** @private */
464
- toHtml() {
461
+ toHtmlInternal() {
465
462
  const { type, name, tag, lastChild } = this;
466
463
  if (type === 'ext-attr' && !(tag in htmlAttrs)
467
464
  || !htmlAttrs[tag]?.has(name) && (tag === 'meta' || tag === 'link' || !commonHtmlAttrs.has(name))) {
468
465
  return '';
469
466
  }
470
- let value = lastChild.toHtml().trim();
467
+ let value = lastChild.toHtmlInternal().trim();
471
468
  if (name === 'style' && insecureStyle.test(value) || name === 'tabindex' && value !== '0') {
472
469
  return '';
473
470
  }
474
471
  else if (name === 'id') {
475
472
  value = value.replace(/\s+/gu, '_');
476
473
  }
477
- return `${name}="${value.replace(/["\n]/gu, p => p === '"' ? '&quot;' : '&#10;')}"`;
474
+ return `${name}="${(0, string_1.sanitizeAttr)(value)}"`;
478
475
  }
479
476
  };
480
477
  return AttributeToken = _classThis;