wikiparser-node 1.6.1 → 1.6.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 (45) hide show
  1. package/dist/addon/table.js +37 -37
  2. package/dist/base.d.ts +0 -3
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.js +12 -14
  5. package/dist/lib/element.d.ts +5 -5
  6. package/dist/lib/element.js +25 -22
  7. package/dist/lib/range.js +1 -1
  8. package/dist/lib/ranges.d.ts +3 -1
  9. package/dist/lib/ranges.js +34 -29
  10. package/dist/lib/text.js +2 -2
  11. package/dist/lib/title.d.ts +8 -6
  12. package/dist/lib/title.js +62 -43
  13. package/dist/parser/braces.js +1 -2
  14. package/dist/parser/commentAndExt.js +1 -2
  15. package/dist/parser/converter.js +1 -2
  16. package/dist/parser/externalLinks.js +18 -5
  17. package/dist/parser/hrAndDoubleUnderscore.js +1 -2
  18. package/dist/parser/html.js +7 -8
  19. package/dist/parser/links.js +6 -4
  20. package/dist/parser/list.js +1 -2
  21. package/dist/parser/magicLinks.js +3 -4
  22. package/dist/parser/quotes.js +1 -2
  23. package/dist/parser/selector.js +51 -23
  24. package/dist/parser/table.js +6 -7
  25. package/dist/src/attribute.js +1 -1
  26. package/dist/src/extLink.js +5 -2
  27. package/dist/src/imageParameter.js +2 -2
  28. package/dist/src/imagemap.js +1 -1
  29. package/dist/src/index.js +3 -4
  30. package/dist/src/link/base.js +1 -1
  31. package/dist/src/link/galleryImage.js +2 -1
  32. package/dist/src/magicLink.js +1 -1
  33. package/dist/src/nowiki/quote.js +1 -1
  34. package/dist/src/paramTag/inputbox.js +2 -2
  35. package/dist/src/parameter.js +1 -1
  36. package/dist/src/table/base.js +1 -1
  37. package/dist/src/table/index.js +7 -5
  38. package/dist/src/table/td.js +3 -2
  39. package/dist/src/table/trBase.js +1 -1
  40. package/dist/src/transclude.js +8 -8
  41. package/dist/util/constants.js +1 -2
  42. package/dist/util/debug.js +2 -9
  43. package/dist/util/diff.js +2 -2
  44. package/dist/util/string.js +8 -2
  45. package/package.json +2 -1
package/dist/lib/title.js CHANGED
@@ -4,17 +4,22 @@ 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'));
7
12
  /** MediaWiki页面标题对象 */
8
13
  class Title {
9
- valid;
14
+ #main;
15
+ #namespaces;
10
16
  ns;
11
17
  fragment;
18
+ interwiki = '';
19
+ valid;
12
20
  /** @private */
13
21
  encoded = false;
14
- #main;
15
22
  /* NOT FOR BROWSER */
16
- #namespaces;
17
- interwiki = '';
18
23
  /** @private */
19
24
  conversionTable = new Map();
20
25
  /** @private */
@@ -28,16 +33,6 @@ class Title {
28
33
  title = title.replace(/_/gu, ' ').trim();
29
34
  this.#main = title && `${title[0].toUpperCase()}${title.slice(1)}`;
30
35
  }
31
- /** 扩展名 */
32
- get extension() {
33
- const { main } = this, i = main.lastIndexOf('.');
34
- return i === -1 ? undefined : main.slice(i + 1).toLowerCase();
35
- }
36
- /* NOT FOR BROWSER */
37
- set extension(extension) {
38
- const { main } = this, i = main.lastIndexOf('.');
39
- this.main = `${i === -1 ? main : main.slice(0, i)}.${extension}`;
40
- }
41
36
  /** 命名空间前缀 */
42
37
  get prefix() {
43
38
  const namespace = this.#namespaces[this.ns];
@@ -47,82 +42,106 @@ class Title {
47
42
  get title() {
48
43
  const prefix = `${this.interwiki}${this.interwiki && ':'}${this.prefix}`;
49
44
  let title = `${prefix}${this.main}`.replace(/ /gu, '_');
50
- const redirected = this.redirects.get(title);
45
+ /* NOT FOR BROWSER */
46
+ let redirected = this.redirects.get(title);
51
47
  if (redirected) {
52
48
  return redirected;
53
49
  }
54
50
  this.autoConvert();
55
51
  title = `${prefix}${this.main}`.replace(/ /gu, '_');
56
- return this.redirects.get(title) ?? title;
52
+ redirected = this.redirects.get(title);
53
+ if (redirected) {
54
+ return redirected;
55
+ }
56
+ /* NOT FOR BROWSER END */
57
+ return title;
58
+ }
59
+ /** 扩展名 */
60
+ get extension() {
61
+ const { main } = this, i = main.lastIndexOf('.');
62
+ return i === -1 ? undefined : main.slice(i + 1).toLowerCase();
63
+ }
64
+ /* NOT FOR BROWSER */
65
+ set extension(extension) {
66
+ const { main } = this, i = main.lastIndexOf('.');
67
+ this.main = `${i === -1 ? main : main.slice(0, i)}.${extension}`;
57
68
  }
58
69
  /* NOT FOR BROWSER END */
59
70
  /**
71
+ * @see MediaWikiTitleCodec::splitTitleString
72
+ *
60
73
  * @param title 标题(含或不含命名空间前缀)
61
74
  * @param defaultNs 命名空间
62
75
  * @param decode 是否需要解码
63
76
  * @param selfLink 是否允许selfLink
64
77
  */
65
- constructor(title, defaultNs = 0, config = index_1.default.getConfig(), decode = false, selfLink = false) {
78
+ constructor(title, defaultNs, config, decode, selfLink) {
79
+ const subpage = title.trim().startsWith('../');
66
80
  title = (0, string_1.decodeHtml)(title);
67
81
  if (decode && title.includes('%')) {
68
82
  try {
69
83
  const encoded = /%(?!21|3[ce]|5[bd]|7[b-d])[\da-f]{2}/iu.test(title);
70
- title = decodeURIComponent(title);
84
+ title = rawurldecode(title);
71
85
  this.encoded = encoded;
72
86
  }
73
87
  catch { }
74
88
  }
75
89
  title = title.replace(/_/gu, ' ').trim();
76
- let ns = defaultNs;
77
- if (title.startsWith(':')) {
78
- ns = 0;
79
- title = title.slice(1).trim();
80
- }
81
- /* NOT FOR BROWSER */
82
- const iw = defaultNs ? null : index_1.default.isInterwiki(title, config);
83
- if (iw) {
84
- this.interwiki = iw[1].toLowerCase();
85
- title = title.slice(iw.indices[0][1]);
90
+ if (subpage) {
91
+ this.ns = 0;
86
92
  }
87
- /* NOT FOR BROWSER END */
88
- const m = title.split(':');
89
- if (m.length > 1) {
90
- const id = config.nsid[m[0].trim().toLowerCase()];
91
- if (id) {
92
- ns = id;
93
- title = m.slice(1).join(':').trim();
93
+ else {
94
+ let ns = defaultNs;
95
+ if (title.startsWith(':')) {
96
+ ns = 0;
97
+ title = title.slice(1).trim();
98
+ }
99
+ /* NOT FOR BROWSER */
100
+ const iw = defaultNs ? null : index_1.default.isInterwiki(title, config);
101
+ if (iw) {
102
+ this.interwiki = iw[1].toLowerCase();
103
+ title = title.slice(iw.indices[0][1]);
104
+ }
105
+ /* NOT FOR BROWSER END */
106
+ const m = title.split(':');
107
+ if (m.length > 1) {
108
+ const id = config.nsid[m[0].trim().toLowerCase()];
109
+ if (id) {
110
+ ns = id;
111
+ title = m.slice(1).join(':').trim();
112
+ }
94
113
  }
114
+ this.ns = ns;
95
115
  }
96
- this.ns = ns;
97
116
  const i = title.indexOf('#');
98
117
  if (i !== -1) {
99
118
  let fragment = title.slice(i + 1).trimEnd();
100
119
  if (fragment.includes('%')) {
101
120
  try {
102
- fragment = decodeURIComponent(fragment);
121
+ fragment = rawurldecode(fragment);
103
122
  }
104
123
  catch { }
105
124
  }
106
125
  this.fragment = fragment;
107
126
  title = title.slice(0, i).trim();
108
127
  }
109
- this.valid = Boolean(title || this.interwiki || selfLink && this.fragment !== undefined)
110
- && !/^:|\0\d+[eh!+-]\x7F|[<>[\]{}|]|%[\da-f]{2}/iu.test(title);
128
+ this.valid = Boolean(title || this.interwiki || selfLink && this.ns === 0 && this.fragment !== undefined)
129
+ && !/^:|\0\d+[eh!+-]\x7F|[<>[\]{}|\n]|%[\da-f]{2}|(?:^|\/)\.{1,2}(?:$|\/)/iu.test(subpage ? /^(?:\.\.\/)+(.*)/u.exec(title)[1] : title);
111
130
  this.main = title;
112
- /* NOT FOR BROWSER */
113
- this.#namespaces = config.namespaces;
114
131
  Object.defineProperties(this, {
115
- valid: { writable: false },
116
132
  encoded: { enumerable: false, writable: false },
133
+ /* NOT FOR BROWSER */
134
+ valid: { writable: false },
117
135
  conversionTable: { enumerable: false },
118
136
  redirects: { enumerable: false },
119
137
  });
138
+ this.#namespaces = config.namespaces;
120
139
  }
121
- /* NOT FOR BROWSER */
122
140
  /** @private */
123
141
  toString() {
124
142
  return `${this.title}${this.fragment === undefined ? '' : `#${this.fragment}`}`;
125
143
  }
144
+ /* NOT FOR BROWSER */
126
145
  /** 执行单向转换 */
127
146
  autoConvert() {
128
147
  const { conversionTable } = this;
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseBraces = void 0;
4
4
  const string_1 = require("../util/string");
5
5
  const constants_1 = require("../util/constants");
6
- const index_1 = require("../index");
7
6
  const heading_1 = require("../src/heading");
8
7
  const transclude_1 = require("../src/transclude");
9
8
  const arg_1 = require("../src/arg");
@@ -14,7 +13,7 @@ const arg_1 = require("../src/arg");
14
13
  * @param accum
15
14
  * @throws TranscludeToken.constructor()
16
15
  */
17
- const parseBraces = (wikitext, config = index_1.default.getConfig(), accum = []) => {
16
+ const parseBraces = (wikitext, config, accum) => {
18
17
  const source = `${config.excludes?.includes('heading') ? '' : '^(\0\\d+c\x7F)*={1,6}|'}\\[\\[|\\{{2,}|-\\{(?!\\{)`, { parserFunction: [, , , subst] } = config, stack = [], closes = { '=': '\n', '{': '\\}{2,}|\\|', '-': '\\}-', '[': '\\]\\]' }, marks = new Map([['!', '!'], ['!!', '+'], ['(!', '{'], ['!)', '}'], ['!-', '-'], ['=', '~']]);
19
18
  let regex = new RegExp(source, 'gmu'), mt = regex.exec(wikitext), moreBraces = wikitext.includes('}}'), lastIndex;
20
19
  while (mt
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseCommentAndExt = void 0;
4
4
  const constants_1 = require("../util/constants");
5
- const index_1 = require("../index");
6
5
  const onlyinclude_1 = require("../src/onlyinclude");
7
6
  const noinclude_1 = require("../src/nowiki/noinclude");
8
7
  const include_1 = require("../src/tagPair/include");
@@ -15,7 +14,7 @@ const comment_1 = require("../src/nowiki/comment");
15
14
  * @param accum
16
15
  * @param includeOnly 是否嵌入
17
16
  */
18
- const parseCommentAndExt = (wikitext, config = index_1.default.getConfig(), accum = [], includeOnly = false) => {
17
+ const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
19
18
  const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', { length } = onlyincludeLeft;
20
19
  /** 更新`<onlyinclude>`和`</onlyinclude>`的位置 */
21
20
  const update = () => {
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseConverter = void 0;
4
4
  const constants_1 = require("../util/constants");
5
- const index_1 = require("../index");
6
5
  const converter_1 = require("../src/converter");
7
6
  /**
8
7
  * 解析语言变体转换
@@ -10,7 +9,7 @@ const converter_1 = require("../src/converter");
10
9
  * @param config
11
10
  * @param accum
12
11
  */
13
- const parseConverter = (text, config = index_1.default.getConfig(), accum = []) => {
12
+ const parseConverter = (text, config, accum) => {
14
13
  const regex1 = /-\{/gu, regex2 = /-\{|\}-/gu, stack = [];
15
14
  let regex = regex1, mt = regex.exec(text);
16
15
  while (mt) {
@@ -3,26 +3,39 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseExternalLinks = void 0;
4
4
  const string_1 = require("../util/string");
5
5
  const constants_1 = require("../util/constants");
6
- const index_1 = require("../index");
7
6
  const extLink_1 = require("../src/extLink");
7
+ const magicLink_1 = require("../src/magicLink");
8
8
  /**
9
9
  * 解析外部链接
10
10
  * @param wikitext
11
11
  * @param config
12
12
  * @param accum
13
+ * @param inFile 是否在图链中
13
14
  */
14
- const parseExternalLinks = (wikitext, config = index_1.default.getConfig(), accum = []) => {
15
+ const parseExternalLinks = (wikitext, config, accum, inFile) => {
15
16
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
16
17
  /\[((?:\[[\da-f:.]+\]|[^[\]\t\n\p{Zs}])[^[\]\t\n\p{Zs}]*(?=[[\]\t\p{Zs}]|\0\d))(\p{Zs}*(?=\P{Zs}))([^\]\n]*)\]/giu;
17
- const regex = new RegExp(`\\[((?:(?:${config.protocol}|//)${string_1.extUrlCharFirst}|\0\\d+m\x7F)${string_1.extUrlChar}(?=[[\\]<>"\\t\\p{Zs}]|\0\\d))`
18
- + '(\\p{Zs}*(?=\\P{Zs}))([^\\]\x01-\x08\x0A-\x1F\uFFFD]*)\\]', 'giu');
18
+ const regex = new RegExp('\\[' // 左括号
19
+ + `(${'\0\\d+f\x7F' // 预先解析的MagicLinkToken
20
+ + '|'
21
+ + `(?:(?:${config.protocol}|//)${string_1.extUrlCharFirst}|\0\\d+m\x7F)${string_1.extUrlChar}(?=[[\\]<>"\\t\\p{Zs}]|\0\\d)`})` // 链接网址
22
+ + '(\\p{Zs}*(?=\\P{Zs}))' // 空格
23
+ + '([^\\]\x01-\x08\x0A-\x1F\uFFFD]*)' // 链接文字
24
+ + '\\]', // 右括号
25
+ 'giu');
19
26
  return wikitext.replace(regex, (_, url, space, text) => {
20
- const { length } = accum, mt = /&[lg]t;/u.exec(url);
27
+ const { length } = accum;
28
+ const mt = /&[lg]t;/u.exec(url);
21
29
  if (mt) {
22
30
  url = url.slice(0, mt.index);
23
31
  space = '';
24
32
  text = `${url.slice(mt.index)}${space}${text}`;
25
33
  }
34
+ if (inFile) {
35
+ // @ts-expect-error abstract class
36
+ new magicLink_1.MagicLinkToken(url, true, config, accum);
37
+ return `[\0${length}f\x7F${space}${text}]`;
38
+ }
26
39
  // @ts-expect-error abstract class
27
40
  new extLink_1.ExtLinkToken(url, space, text, config, accum);
28
41
  return `\0${length}w\x7F`;
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseHrAndDoubleUnderscore = void 0;
4
4
  const constants_1 = require("../util/constants");
5
- const index_1 = require("../index");
6
5
  const hr_1 = require("../src/nowiki/hr");
7
6
  const doubleUnderscore_1 = require("../src/nowiki/doubleUnderscore");
8
7
  const heading_1 = require("../src/heading");
@@ -12,7 +11,7 @@ const heading_1 = require("../src/heading");
12
11
  * @param config
13
12
  * @param accum
14
13
  */
15
- const parseHrAndDoubleUnderscore = ({ firstChild: { data }, type, name }, config = index_1.default.getConfig(), accum = []) => {
14
+ const parseHrAndDoubleUnderscore = ({ firstChild: { data }, type, name }, config, accum) => {
16
15
  const { doubleUnderscore } = config, insensitive = new Set(doubleUnderscore[0]), sensitive = new Set(doubleUnderscore[1]);
17
16
  if (type !== 'root' && (type !== 'ext-inner' || name !== 'poem')) {
18
17
  data = `\0${data}`;
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseHtml = void 0;
4
4
  const constants_1 = require("../util/constants");
5
- const index_1 = require("../index");
6
5
  const attributes_1 = require("../src/attributes");
7
6
  const html_1 = require("../src/html");
8
7
  /**
@@ -11,7 +10,7 @@ const html_1 = require("../src/html");
11
10
  * @param config
12
11
  * @param accum
13
12
  */
14
- const parseHtml = (wikitext, config = index_1.default.getConfig(), accum = []) => {
13
+ const parseHtml = (wikitext, config, accum) => {
15
14
  const regex = /^(\/?)([a-z][^\s/>]*)((?:\s|\/(?!>))[^>]*?)?(\/?>)([^<]*)$/iu, elements = new Set(config.html.flat()), bits = wikitext.split('<');
16
15
  let text = bits.shift();
17
16
  for (const x of bits) {
@@ -20,18 +19,18 @@ const parseHtml = (wikitext, config = index_1.default.getConfig(), accum = []) =
20
19
  text += `<${x}`;
21
20
  continue;
22
21
  }
23
- const [, slash, , params = '', brace, rest] = mt,
22
+ const [, slash, , params = '', brace, rest] = mt, { length } = accum,
24
23
  // @ts-expect-error abstract class
25
- attr = new attributes_1.AttributesToken(params, 'html-attrs', name, config, accum), itemprop = attr.getAttr('itemprop');
26
- if (name === 'meta' && (itemprop === undefined || attr.getAttr('content') === undefined)
27
- || name === 'link' && (itemprop === undefined || attr.getAttr('href') === undefined)) {
24
+ attrs = new attributes_1.AttributesToken(params, 'html-attrs', name, config, accum), itemprop = attrs.getAttr('itemprop');
25
+ if (name === 'meta' && (itemprop === undefined || attrs.getAttr('content') === undefined)
26
+ || name === 'link' && (itemprop === undefined || attrs.getAttr('href') === undefined)) {
28
27
  text += `<${x}`;
29
- accum.pop();
28
+ accum.length = length;
30
29
  continue;
31
30
  }
32
31
  text += `\0${accum.length}x\x7F${rest}`;
33
32
  // @ts-expect-error abstract class
34
- new html_1.HtmlToken(t, attr, slash === '/', brace === '/>', config, accum);
33
+ new html_1.HtmlToken(t, attrs, slash === '/', brace === '/>', config, accum);
35
34
  }
36
35
  return text;
37
36
  };
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseLinks = void 0;
4
4
  const constants_1 = require("../util/constants");
5
5
  const index_1 = require("../index");
6
+ const quotes_1 = require("./quotes");
7
+ const externalLinks_1 = require("./externalLinks");
6
8
  const index_2 = require("../src/link/index");
7
9
  const file_1 = require("../src/link/file");
8
10
  const category_1 = require("../src/link/category");
@@ -12,8 +14,7 @@ const category_1 = require("../src/link/category");
12
14
  * @param config
13
15
  * @param accum
14
16
  */
15
- const parseLinks = (wikitext, config = index_1.default.getConfig(), accum = []) => {
16
- const { parseQuotes } = require('./quotes');
17
+ const parseLinks = (wikitext, config, accum) => {
17
18
  const regex = /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(?:(\||\0\d+!\x7F)(.*?[^\]]))?\]\](.*)$/su, regexImg = /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(\||\0\d+!\x7F)(.*)$/su, regexExt = new RegExp(`^\\s*(?:${config.protocol}|//)`, 'iu'), bits = wikitext.split('[[');
18
19
  let s = bits.shift();
19
20
  for (let i = 0; i < bits.length; i++) {
@@ -75,17 +76,18 @@ const parseLinks = (wikitext, config = index_1.default.getConfig(), accum = [])
75
76
  continue;
76
77
  }
77
78
  }
78
- text &&= parseQuotes(text, config, accum);
79
- s += `\0${accum.length}l\x7F${after}`;
79
+ text &&= (0, quotes_1.parseQuotes)(text, config, accum);
80
80
  let SomeLinkToken = index_2.LinkToken;
81
81
  if (!force) {
82
82
  if (!interwiki && ns === 6) {
83
+ text &&= (0, externalLinks_1.parseExternalLinks)(text, config, accum, true);
83
84
  SomeLinkToken = file_1.FileToken;
84
85
  }
85
86
  else if (!interwiki && ns === 14) {
86
87
  SomeLinkToken = category_1.CategoryToken;
87
88
  }
88
89
  }
90
+ s += `\0${accum.length}l\x7F${after}`;
89
91
  // @ts-expect-error abstract class
90
92
  new SomeLinkToken(link, text, config, accum, delimiter);
91
93
  }
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseList = void 0;
4
4
  const constants_1 = require("../util/constants");
5
- const index_1 = require("../index");
6
5
  const list_1 = require("../src/nowiki/list");
7
6
  const dd_1 = require("../src/nowiki/dd");
8
7
  /**
@@ -11,7 +10,7 @@ const dd_1 = require("../src/nowiki/dd");
11
10
  * @param config
12
11
  * @param accum
13
12
  */
14
- const parseList = (wikitext, config = index_1.default.getConfig(), accum = []) => {
13
+ const parseList = (wikitext, config, accum) => {
15
14
  const mt = /^((?:\0\d+c\x7F)*)([;:*#]+)/u.exec(wikitext);
16
15
  if (!mt) {
17
16
  return wikitext;
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseMagicLinks = void 0;
4
4
  const string_1 = require("../util/string");
5
5
  const constants_1 = require("../util/constants");
6
- const index_1 = require("../index");
7
6
  const magicLink_1 = require("../src/magicLink");
8
7
  /**
9
8
  * 解析自由外链
@@ -11,18 +10,18 @@ const magicLink_1 = require("../src/magicLink");
11
10
  * @param config
12
11
  * @param accum
13
12
  */
14
- const parseMagicLinks = (wikitext, config = index_1.default.getConfig(), accum = []) => {
13
+ const parseMagicLinks = (wikitext, config, accum) => {
15
14
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
16
15
  /(^|[^\p{L}\d_])((?:\[[\da-f:.]+\]|[^[\]<>"\t\n\p{Zs}])(?:[^[\]<>"\0\t\n\p{Zs}]|\0\d+c\x7F)*)/giu;
17
16
  const regex = new RegExp(`(^|[^\\p{L}\\d_])(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})`, 'giu');
18
17
  return wikitext.replace(regex, (m, lead, p1) => {
19
- let trail = '', url = lead ? m.slice(1) : m;
18
+ let trail = '', url = lead ? m.slice(lead.length) : m;
20
19
  const m2 = /&(?:lt|gt|nbsp|#x0*(?:3[ce]|a0)|#0*(?:6[02]|160));/iu.exec(url);
21
20
  if (m2) {
22
21
  trail = url.slice(m2.index);
23
22
  url = url.slice(0, m2.index);
24
23
  }
25
- const sep = new RegExp(`[,;.:!?${url.includes('(') ? '' : ')'}]+$`, 'u'), sepChars = sep.exec(url);
24
+ const sep = new RegExp(`[,;\\\\.:!?${url.includes('(') ? '' : ')'}]+$`, 'u'), sepChars = sep.exec(url);
26
25
  if (sepChars) {
27
26
  let correction = 0;
28
27
  if (sepChars[0].startsWith(';') && /&(?:[a-z]+|#x[\da-f]+|#\d+)$/iu.test(url.slice(0, sepChars.index))) {
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseQuotes = void 0;
4
4
  const constants_1 = require("../util/constants");
5
- const index_1 = require("../index");
6
5
  const quote_1 = require("../src/nowiki/quote");
7
6
  /**
8
7
  * 解析单引号
@@ -10,7 +9,7 @@ const quote_1 = require("../src/nowiki/quote");
10
9
  * @param config
11
10
  * @param accum
12
11
  */
13
- const parseQuotes = (wikitext, config = index_1.default.getConfig(), accum = []) => {
12
+ const parseQuotes = (wikitext, config, accum) => {
14
13
  const arr = wikitext.split(/('{2,})/u), { length } = arr;
15
14
  if (length === 1) {
16
15
  return wikitext;
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseSelector = void 0;
4
4
  const constants_1 = require("../util/constants");
5
5
  const string_1 = require("../util/string");
6
- const index_1 = require("../index");
7
6
  const simplePseudos = new Set([
8
7
  'root',
9
8
  'first-child',
@@ -24,7 +23,7 @@ const simplePseudos = new Set([
24
23
  'required',
25
24
  'optional',
26
25
  ]);
27
- const complexPseudos = [
26
+ const complexPseudos = new Set([
28
27
  'is',
29
28
  'not',
30
29
  'nth-child',
@@ -35,7 +34,7 @@ const complexPseudos = [
35
34
  'has',
36
35
  'lang',
37
36
  'regex',
38
- ];
37
+ ]);
39
38
  const specialChars = [
40
39
  ['[', '&lbrack;'],
41
40
  [']', '&rbrack;'],
@@ -47,7 +46,7 @@ const specialChars = [
47
46
  ['\\', '&bsol;'],
48
47
  ['&', '&amp;'],
49
48
  ];
50
- const pseudoRegex = new RegExp(`:(${complexPseudos.join('|')})$`, 'u'), regularRegex = /[[(,>+~]|\s+/u, attributeRegex = /^\s*(\w+)\s*(?:([~|^$*!]?=)\s*("[^"]*"|'[^']*'|[^\s[\]]+)(?:\s+(i))?\s*)?\]/u, functionRegex = /^(\s*"[^"]*"\s*|\s*'[^']*'\s*|[^()]*)\)/u, grouping = new Set([',', '>', '+', '~']), combinator = new Set(['>', '+', '~', '']);
49
+ const regularRegex = /[[(,>+~]|\s+/u, attributeRegex = /^\s*(\w+)\s*(?:([~|^$*!]?=)\s*("[^"]*"|'[^']*'|[^\s[\]]+)(?:\s+(i))?\s*)?\]/u, functionRegex = /^(\s*"[^"]*"\s*|\s*'[^']*'\s*|[^()]*)\)/u, grouping = new Set([',', '>', '+', '~']), combinator = new Set(['>', '+', '~', '']);
51
50
  /**
52
51
  * 清理转义符号
53
52
  * @param selector
@@ -85,16 +84,49 @@ const parseSelector = (selector) => {
85
84
  /**
86
85
  * 解析简单伪选择器
87
86
  * @param index 伪选择器的终点位置
87
+ * @throws `SyntaxError` 选择器排序
88
88
  * @throws `SyntaxError` 非法的选择器
89
89
  */
90
90
  const pushSimple = (index) => {
91
- const str = sanitized.slice(0, index), pieces = str.trim().split(':'),
92
- // eslint-disable-next-line unicorn/explicit-length-check
93
- i = pieces.slice(1).findIndex(pseudo => simplePseudos.has(pseudo)) + 1 || pieces.length;
94
- if (pieces.slice(i).some(pseudo => !simplePseudos.has(pseudo))) {
95
- throw new SyntaxError(`非法的选择器!\n${str}\n可能需要将':'转义为'\\:'。`);
91
+ const str = sanitized.slice(0, index).trim();
92
+ if (!str) {
93
+ return;
94
+ }
95
+ const pieces = str.split(/(?=[:#])/u);
96
+ for (let i = 0; i < pieces.length; i++) {
97
+ const piece = pieces[i];
98
+ if (!/^[:#]/u.test(piece)) {
99
+ if (step.length > 0) {
100
+ throw new SyntaxError(`Invalid selector!\n${selector}\nType selectors must come first.`);
101
+ }
102
+ else {
103
+ step.push(piece);
104
+ }
105
+ }
106
+ else if (piece.startsWith(':')) {
107
+ if (simplePseudos.has(piece.slice(1))) {
108
+ step.push(piece);
109
+ }
110
+ else if (pieces[i - 1]?.startsWith('#')) {
111
+ pieces[i - 1] += piece;
112
+ pieces.splice(i, 1);
113
+ i--;
114
+ }
115
+ else {
116
+ throw new SyntaxError(`Non-existing pseudo selector!\n${desanitize(piece)}`);
117
+ }
118
+ }
119
+ }
120
+ step.push(...pieces.filter(piece => piece.startsWith('#')).map(piece => desanitize(piece)));
121
+ };
122
+ /**
123
+ * 检查是否需要通用选择器
124
+ * @throws `SyntaxError` 非法的选择器
125
+ */
126
+ const needUniversal = () => {
127
+ if (step.length === 0) {
128
+ throw new SyntaxError(`Invalid selector!\n${selector}\nYou may need the universal selector '*'.`);
96
129
  }
97
- step.push(desanitize(pieces.slice(0, i).join(':')), ...pieces.slice(i).map(piece => `:${piece}`));
98
130
  };
99
131
  while (mt) {
100
132
  let { 0: syntax, index } = mt;
@@ -105,15 +137,14 @@ const parseSelector = (selector) => {
105
137
  }
106
138
  if (syntax === ',') { // 情形1:并列
107
139
  pushSimple(index);
140
+ needUniversal();
108
141
  condition = [[]];
109
142
  [step] = condition;
110
143
  stack.push(condition);
111
144
  }
112
145
  else if (combinator.has(syntax)) { // 情形2:关系
113
146
  pushSimple(index);
114
- if (!step.some(Boolean)) {
115
- throw new SyntaxError(`非法的选择器!\n${selector}\n可能需要通用选择器'*'。`);
116
- }
147
+ needUniversal();
117
148
  step.relation = syntax;
118
149
  step = [];
119
150
  condition.push(step);
@@ -128,12 +159,12 @@ const parseSelector = (selector) => {
128
159
  regex = regularRegex;
129
160
  }
130
161
  else if (syntax === '(') { // 情形5:伪选择器开启
131
- const pseudoExec = pseudoRegex.exec(sanitized.slice(0, index));
132
- if (!pseudoExec) {
133
- throw new SyntaxError(`非法的选择器!\n${desanitize(sanitized)}\n请检查伪选择器是否存在。`);
162
+ const i = sanitized.lastIndexOf(':', index), pseudo = sanitized.slice(i + 1, index);
163
+ if (i === -1 || !complexPseudos.has(pseudo)) {
164
+ throw new SyntaxError(`Non-existing pseudo selector!\n${desanitize(sanitized)}`);
134
165
  }
135
- pushSimple(pseudoExec.index);
136
- step.push(pseudoExec[1]); // 临时存放复杂伪选择器
166
+ pushSimple(i);
167
+ step.push(pseudo); // 临时存放复杂伪选择器
137
168
  regex = functionRegex;
138
169
  }
139
170
  else { // 情形6:伪选择器闭合
@@ -150,13 +181,10 @@ const parseSelector = (selector) => {
150
181
  }
151
182
  if (regex === regularRegex) {
152
183
  pushSimple(Infinity);
153
- const pseudos = new Set(stack.flat(2).filter((e) => typeof e === 'string' && e.startsWith(':')));
154
- if (pseudos.size > 0) {
155
- index_1.default.warn('检测到伪选择器,请确认是否需要将":"转义成"\\:"。', pseudos);
156
- }
184
+ needUniversal();
157
185
  return stack;
158
186
  }
159
- throw new SyntaxError(`非法的选择器!\n${selector}\n检测到未闭合的'${regex === attributeRegex ? '[' : '('}'`);
187
+ throw new SyntaxError(`Unclosed '${regex === attributeRegex ? '[' : '('}' in the selector!\n${desanitize(sanitized)}`);
160
188
  };
161
189
  exports.parseSelector = parseSelector;
162
190
  constants_1.parsers['parseSelector'] = __filename;
@@ -2,9 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseTable = void 0;
4
4
  const constants_1 = require("../util/constants");
5
- const index_1 = require("../index");
6
- const index_2 = require("../src/index");
7
- const index_3 = require("../src/table/index");
5
+ const index_1 = require("../src/index");
6
+ const index_2 = require("../src/table/index");
8
7
  const tr_1 = require("../src/table/tr");
9
8
  const td_1 = require("../src/table/td");
10
9
  const dd_1 = require("../src/nowiki/dd");
@@ -12,14 +11,14 @@ const dd_1 = require("../src/nowiki/dd");
12
11
  * 判断是否为表格行或表格
13
12
  * @param token 表格节点
14
13
  */
15
- const isTr = (token) => token.lastChild.constructor !== index_2.Token;
14
+ const isTr = (token) => token.lastChild.constructor !== index_1.Token;
16
15
  /**
17
16
  * 解析表格,注意`tr`和`td`包含开头的换行
18
17
  * @param {Token & {firstChild: AstText}} root 根节点
19
18
  * @param config
20
19
  * @param accum
21
20
  */
22
- const parseTable = ({ firstChild: { data }, type, name }, config = index_1.default.getConfig(), accum = []) => {
21
+ const parseTable = ({ firstChild: { data }, type, name }, config, accum) => {
23
22
  const stack = [], lines = data.split('\n');
24
23
  let out = type === 'root' || type === 'parameter-value' || type === 'ext-inner' && name === 'poem'
25
24
  ? ''
@@ -36,7 +35,7 @@ const parseTable = ({ firstChild: { data }, type, name }, config = index_1.defau
36
35
  }
37
36
  const { lastChild } = topToken;
38
37
  if (isTr(topToken)) {
39
- const token = new index_2.Token(str, config, accum);
38
+ const token = new index_1.Token(str, config, accum);
40
39
  token.type = 'table-inter';
41
40
  token.setAttribute('stage', 3);
42
41
  topToken.insertAt(token);
@@ -62,7 +61,7 @@ const parseTable = ({ firstChild: { data }, type, name }, config = index_1.defau
62
61
  }
63
62
  push(`\n${spaces}${indent && `\0${accum.length - 1}d\x7F`}${moreSpaces}\0${accum.length}b\x7F`, top);
64
63
  // @ts-expect-error abstract class
65
- stack.push(...top ? [top] : [], new index_3.TableToken(tableSyntax, attr, config, accum));
64
+ stack.push(...top ? [top] : [], new index_2.TableToken(tableSyntax, attr, config, accum));
66
65
  continue;
67
66
  }
68
67
  else if (!top) {
@@ -417,7 +417,7 @@ let AttributeToken = (() => {
417
417
  /** @override */
418
418
  print() {
419
419
  const [quoteStart = '', quoteEnd = ''] = this.#quotes;
420
- return this.#equal ? super.print({ sep: `${this.#equal}${quoteStart}`, post: quoteEnd }) : super.print();
420
+ return this.#equal ? super.print({ sep: `${(0, string_1.escape)(this.#equal)}${quoteStart}`, post: quoteEnd }) : super.print();
421
421
  }
422
422
  /* NOT FOR BROWSER */
423
423
  /** @private */