wikilint 2.9.3 → 2.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 (65) hide show
  1. package/config/.schema.json +5 -0
  2. package/config/enwiki.json +2 -1
  3. package/config/llwiki.json +2 -1
  4. package/config/moegirl.json +2 -1
  5. package/config/zhwiki.json +2 -1
  6. package/dist/base.d.ts +3 -2
  7. package/dist/index.js +21 -18
  8. package/dist/lib/element.js +2 -2
  9. package/dist/lib/node.d.ts +6 -1
  10. package/dist/lib/node.js +7 -0
  11. package/dist/lib/text.d.ts +0 -2
  12. package/dist/lib/text.js +8 -10
  13. package/dist/lib/title.js +4 -9
  14. package/dist/mixin/hidden.d.ts +1 -0
  15. package/dist/mixin/hidden.js +2 -2
  16. package/dist/parser/list.js +23 -6
  17. package/dist/parser/magicLinks.js +1 -2
  18. package/dist/parser/table.js +2 -2
  19. package/dist/src/arg.js +3 -6
  20. package/dist/src/attribute.js +9 -28
  21. package/dist/src/attributes.js +2 -3
  22. package/dist/src/converter.d.ts +1 -1
  23. package/dist/src/converter.js +4 -3
  24. package/dist/src/converterFlags.js +4 -7
  25. package/dist/src/converterRule.js +9 -13
  26. package/dist/src/extLink.js +3 -3
  27. package/dist/src/gallery.js +6 -5
  28. package/dist/src/heading.js +4 -3
  29. package/dist/src/hidden.js +1 -1
  30. package/dist/src/html.js +3 -6
  31. package/dist/src/imageParameter.js +8 -11
  32. package/dist/src/imagemap.js +2 -2
  33. package/dist/src/index.js +12 -11
  34. package/dist/src/link/base.js +15 -13
  35. package/dist/src/link/file.d.ts +0 -7
  36. package/dist/src/link/file.js +19 -25
  37. package/dist/src/link/redirectTarget.d.ts +1 -1
  38. package/dist/src/link/redirectTarget.js +3 -10
  39. package/dist/src/magicLink.d.ts +2 -0
  40. package/dist/src/magicLink.js +37 -53
  41. package/dist/src/nested.js +1 -1
  42. package/dist/src/nowiki/comment.js +4 -7
  43. package/dist/src/nowiki/doubleUnderscore.js +1 -1
  44. package/dist/src/nowiki/index.js +1 -4
  45. package/dist/src/nowiki/noinclude.js +5 -1
  46. package/dist/src/nowiki/quote.js +5 -0
  47. package/dist/src/onlyinclude.js +2 -2
  48. package/dist/src/paramTag/index.js +2 -2
  49. package/dist/src/parameter.js +11 -13
  50. package/dist/src/redirect.js +3 -3
  51. package/dist/src/syntax.d.ts +1 -1
  52. package/dist/src/syntax.js +2 -2
  53. package/dist/src/table/td.js +2 -2
  54. package/dist/src/table/trBase.js +3 -4
  55. package/dist/src/tagPair/ext.js +3 -3
  56. package/dist/src/tagPair/include.js +5 -1
  57. package/dist/src/tagPair/index.js +3 -3
  58. package/dist/src/transclude.js +16 -15
  59. package/dist/util/html.js +10 -0
  60. package/dist/util/lint.js +1 -1
  61. package/dist/util/string.js +11 -3
  62. package/errors/README +2 -0
  63. package/i18n/zh-hans.json +1 -1
  64. package/i18n/zh-hant.json +1 -1
  65. package/package.json +11 -11
@@ -162,6 +162,11 @@
162
162
  "minItems": 2,
163
163
  "maxItems": 2
164
164
  }
165
+ },
166
+ "articlePath": {
167
+ "description": "base URL of internal links",
168
+ "type": "string",
169
+ "pattern": "\\$1"
165
170
  }
166
171
  },
167
172
  "required": [
@@ -409,5 +409,6 @@
409
409
  "redirection": [
410
410
  "#redirect"
411
411
  ],
412
- "variants": []
412
+ "variants": [],
413
+ "articlePath": "/wiki/$1"
413
414
  }
@@ -597,5 +597,6 @@
597
597
  "zh-sg",
598
598
  "zh-my",
599
599
  "zh-mo"
600
- ]
600
+ ],
601
+ "articlePath": "/zh/$1"
601
602
  }
@@ -687,5 +687,6 @@
687
687
  "zh-cn",
688
688
  "zh-tw",
689
689
  "zh-hk"
690
- ]
690
+ ],
691
+ "articlePath": "/$1"
691
692
  }
@@ -805,5 +805,6 @@
805
805
  "zh-sg",
806
806
  "zh-my",
807
807
  "zh-mo"
808
- ]
808
+ ],
809
+ "articlePath": "/wiki/$1"
809
810
  }
package/dist/base.d.ts CHANGED
@@ -11,7 +11,7 @@ export interface Config {
11
11
  readonly variants: string[];
12
12
  readonly excludes?: string[];
13
13
  }
14
- 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';
14
+ 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';
15
15
  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"];
16
16
  export declare namespace LintError {
17
17
  type Severity = 'error' | 'warning';
@@ -38,8 +38,9 @@ export interface LintError {
38
38
  }
39
39
  /** 类似Node */
40
40
  export interface AstNode {
41
- type: string;
42
41
  readonly childNodes: readonly AstNode[];
42
+ /** 节点类型 */
43
+ type: string;
43
44
  /** Linter */
44
45
  lint(): LintError[];
45
46
  }
package/dist/index.js CHANGED
@@ -11,8 +11,7 @@ const string_1 = require("./util/string");
11
11
  * @param file 文件名
12
12
  * @param dir 子路径
13
13
  */
14
- const rootRequire = (file, dir) => require(file.startsWith('/') ? file : `../${file.includes('/') ? '' : dir}${file}`);
15
- // eslint-disable-next-line @typescript-eslint/no-redeclare
14
+ const rootRequire = (file, dir) => require(path.isAbsolute(file) ? file : path.join('..', file.includes('/') ? '' : dir, file));
16
15
  const Parser = {
17
16
  config: 'default',
18
17
  i18n: undefined,
@@ -20,7 +19,7 @@ const Parser = {
20
19
  /** @implements */
21
20
  getConfig() {
22
21
  if (typeof this.config === 'string') {
23
- this.config = rootRequire(this.config, 'config/');
22
+ this.config = rootRequire(this.config, 'config');
24
23
  return this.getConfig();
25
24
  }
26
25
  return {
@@ -31,7 +30,7 @@ const Parser = {
31
30
  /** @implements */
32
31
  msg(msg, arg = '') {
33
32
  if (typeof this.i18n === 'string') {
34
- this.i18n = rootRequire(this.i18n, 'i18n/');
33
+ this.i18n = rootRequire(this.i18n, 'i18n');
35
34
  return this.msg(msg, arg);
36
35
  }
37
36
  return msg && (this.i18n?.[msg] ?? msg).replace('$1', this.msg(arg));
@@ -39,23 +38,27 @@ const Parser = {
39
38
  /** @implements */
40
39
  normalizeTitle(title, defaultNs = 0, include, config = Parser.getConfig(), halfParsed, decode = false, selfLink = false) {
41
40
  const { Title } = require('./lib/title');
41
+ let titleObj;
42
42
  if (halfParsed) {
43
- return new Title(title, defaultNs, config, decode, selfLink);
43
+ titleObj = new Title(title, defaultNs, config, decode, selfLink);
44
44
  }
45
- const { Token } = require('./src/index');
46
- return debug_1.Shadow.run(() => {
47
- const root = new Token(title, config);
48
- root.type = 'root';
49
- root.parseOnce(0, include).parseOnce();
50
- const titleObj = new Title(root.toString(), defaultNs, config, decode, selfLink);
51
- for (const key of ['main', 'fragment']) {
52
- const str = titleObj[key];
53
- if (str?.includes('\0')) {
54
- titleObj[key] = root.buildFromStr(str, constants_1.BuildMethod.Text);
45
+ else {
46
+ const { Token } = require('./src/index');
47
+ titleObj = debug_1.Shadow.run(() => {
48
+ const root = new Token(title, config);
49
+ root.type = 'root';
50
+ root.parseOnce(0, include).parseOnce();
51
+ const t = new Title(root.toString(), defaultNs, config, decode, selfLink);
52
+ for (const key of ['main', 'fragment']) {
53
+ const str = t[key];
54
+ if (str?.includes('\0')) {
55
+ t[key] = root.buildFromStr(str, constants_1.BuildMethod.Text);
56
+ }
55
57
  }
56
- }
57
- return titleObj;
58
- });
58
+ return t;
59
+ });
60
+ }
61
+ return titleObj;
59
62
  },
60
63
  /** @implements */
61
64
  parse(wikitext, include, maxStage = constants_1.MAX_STAGE, config = Parser.getConfig()) {
@@ -158,8 +158,8 @@ class AstElement extends node_1.AstNode {
158
158
  return data;
159
159
  }
160
160
  /** @private */
161
- toString(separator = '') {
162
- return this.childNodes.map(String).join(separator);
161
+ toString(skip, separator = '') {
162
+ return this.childNodes.map(child => child.toString(skip)).join(separator);
163
163
  }
164
164
  /** @private */
165
165
  lint(start = this.getAbsoluteIndex(), re) {
@@ -18,9 +18,9 @@ export declare abstract class AstNode implements AstNodeBase {
18
18
  #private;
19
19
  data?: string | undefined;
20
20
  readonly childNodes: readonly AstNodes[];
21
- /** 节点类型 */
22
21
  abstract get type(): TokenTypes | 'text';
23
22
  abstract set type(value: TokenTypes | 'text');
23
+ /** 可见部分 */
24
24
  text(): string;
25
25
  lint(): LintError[];
26
26
  /** 首位子节点 */
@@ -51,4 +51,9 @@ export declare abstract class AstNode implements AstNodeBase {
51
51
  getRelativeIndex(j?: number): number;
52
52
  /** 获取当前节点的绝对位置 */
53
53
  getAbsoluteIndex(): number;
54
+ /**
55
+ * 是否是某种类型的节点
56
+ * @param type 节点类型
57
+ */
58
+ is<T extends Token>(type: string): this is T;
54
59
  }
package/dist/lib/node.js CHANGED
@@ -114,5 +114,12 @@ class AstNode {
114
114
  configurable: true,
115
115
  });
116
116
  }
117
+ /**
118
+ * 是否是某种类型的节点
119
+ * @param type 节点类型
120
+ */
121
+ is(type) {
122
+ return this.type === type;
123
+ }
117
124
  }
118
125
  exports.AstNode = AstNode;
@@ -8,8 +8,6 @@ export declare class AstText extends AstNode {
8
8
  get type(): 'text';
9
9
  /** @param text 包含文本 */
10
10
  constructor(text: string);
11
- /** 可见部分 */
12
- text(): string;
13
11
  /**
14
12
  * 替换字符串
15
13
  * @param text 替换的字符串
package/dist/lib/text.js CHANGED
@@ -60,15 +60,18 @@ class AstText extends node_1.AstNode {
60
60
  });
61
61
  }
62
62
  /** @private */
63
- toString() {
64
- return this.data;
63
+ toString(skip) {
64
+ return skip && !this.parentNode?.getAttribute('built') ? (0, string_1.removeComment)(this.data) : this.data;
65
65
  }
66
- /** 可见部分 */
66
+ /** @private */
67
67
  text() {
68
68
  return this.data;
69
69
  }
70
70
  /** @private */
71
71
  lint(start = this.getAbsoluteIndex(), errorRegex) {
72
+ if (errorRegex === false) {
73
+ return [];
74
+ }
72
75
  const { data, parentNode, nextSibling, previousSibling } = this;
73
76
  if (!parentNode) {
74
77
  throw new Error('An isolated text node cannot be linted!');
@@ -108,9 +111,7 @@ class AstText extends node_1.AstNode {
108
111
  || char === '['
109
112
  && type === 'ext-link-text'
110
113
  && (/&(?:rbrack|#93|#x5[Dd];);/u.test(data.slice(index + 1))
111
- || nextType === 'ext'
112
- && nextName === 'nowiki'
113
- && nextSibling.innerText?.includes(']'))) {
114
+ || nextSibling?.is('ext') && nextName === 'nowiki' && nextSibling.innerText?.includes(']'))) {
114
115
  continue;
115
116
  }
116
117
  else if (char === ']' && (index || length > 1)) {
@@ -191,10 +192,7 @@ class AstText extends node_1.AstNode {
191
192
  }
192
193
  else if (char === ']' && previousType === 'free-ext-link' && severity === 'error') {
193
194
  const i = start - previousSibling.toString().length;
194
- e.fix = {
195
- range: [i, i],
196
- text: '[',
197
- };
195
+ e.fix = { range: [i, i], text: '[' };
198
196
  }
199
197
  errors.push(e);
200
198
  }
package/dist/lib/title.js CHANGED
@@ -2,11 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Title = void 0;
4
4
  const string_1 = require("../util/string");
5
- /**
6
- * PHP的`rawurldecode`函数的JavaScript实现
7
- * @param str 要解码的字符串
8
- */
9
- const rawurldecode = (str) => decodeURIComponent(str.replace(/%(?![\da-f]{2})/giu, '%25'));
10
5
  /** MediaWiki页面标题对象 */
11
6
  class Title {
12
7
  #main;
@@ -56,12 +51,12 @@ class Title {
56
51
  if (decode && title.includes('%')) {
57
52
  try {
58
53
  const encoded = /%(?!21|3[ce]|5[bd]|7[b-d])[\da-f]{2}/iu.test(title);
59
- title = rawurldecode(title);
54
+ title = (0, string_1.rawurldecode)(title);
60
55
  this.encoded = encoded;
61
56
  }
62
57
  catch { }
63
58
  }
64
- title = title.replace(/_/gu, ' ').trim();
59
+ title = title.replace(/[_ ]+/gu, ' ').trim();
65
60
  if (subpage) {
66
61
  this.ns = 0;
67
62
  }
@@ -73,7 +68,7 @@ class Title {
73
68
  }
74
69
  const m = title.split(':');
75
70
  if (m.length > 1) {
76
- const id = config.nsid[m[0].trim().toLowerCase()];
71
+ const k = m[0].trim().toLowerCase(), id = Object.prototype.hasOwnProperty.call(config.nsid, k) && config.nsid[k];
77
72
  if (id) {
78
73
  ns = id;
79
74
  title = m.slice(1).join(':').trim();
@@ -86,7 +81,7 @@ class Title {
86
81
  let fragment = title.slice(i + 1).trimEnd();
87
82
  if (fragment.includes('%')) {
88
83
  try {
89
- fragment = rawurldecode(fragment);
84
+ fragment = (0, string_1.rawurldecode)(fragment);
90
85
  }
91
86
  catch { }
92
87
  }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * 解析后不可见的类
3
3
  * @param linter 是否覆写 lint 方法
4
+ * @param html 是否覆写 toHtml 方法
4
5
  * @param constructor 基类
5
6
  * @param _ context
6
7
  */
@@ -5,17 +5,17 @@ const debug_1 = require("../util/debug");
5
5
  /**
6
6
  * 解析后不可见的类
7
7
  * @param linter 是否覆写 lint 方法
8
+ * @param html 是否覆写 toHtml 方法
8
9
  * @param constructor 基类
9
10
  * @param _ context
10
11
  */
11
- const hiddenToken = (linter) => (constructor, _) => {
12
+ const hiddenToken = (linter = true, html = true) => (constructor, _) => {
12
13
  /** 解析后不可见的类 */
13
14
  class AnyHiddenToken extends constructor {
14
15
  /** 没有可见部分 */
15
16
  text() {
16
17
  return '';
17
18
  }
18
- /** @private */
19
19
  lint(start) {
20
20
  // @ts-expect-error private argument
21
21
  return linter ? [] : super.lint(start);
@@ -1,23 +1,40 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseList = void 0;
4
+ const html_1 = require("../util/html");
4
5
  const list_1 = require("../src/nowiki/list");
5
6
  const dd_1 = require("../src/nowiki/dd");
6
7
  /**
7
8
  * 解析列表
8
9
  * @param wikitext
10
+ * @param state
11
+ * @param state.lastPrefix 上一个列表的前缀
9
12
  * @param config
10
13
  * @param accum
11
14
  */
12
- const parseList = (wikitext, config, accum) => {
13
- const mt = /^((?:\0\d+c\x7F)*)([;:*#]+)/u.exec(wikitext);
15
+ const parseList = (wikitext, state, config, accum) => {
16
+ const mt = /^((?:\0\d+c\x7F)*)([;:*#]+\s*)/u.exec(wikitext);
14
17
  if (!mt) {
18
+ state.lastPrefix = '';
15
19
  return wikitext;
16
20
  }
17
- const [total, comment, prefix] = mt;
18
- let text = `${comment}\0${accum.length}d\x7F${wikitext.slice(total.length)}`, dt = prefix.split(';').length - 1;
19
- // @ts-expect-error abstract class
20
- new list_1.ListToken(prefix, config, accum);
21
+ 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(';');
22
+ let dt = parts.length - (isDt ? 0 : 1);
23
+ if (commonPrefixLength > 1) {
24
+ const commonPrefix = prefix.slice(0, commonPrefixLength - 1);
25
+ if (isDt) {
26
+ parts.unshift(commonPrefix);
27
+ }
28
+ else {
29
+ parts[0] = commonPrefix + parts[0];
30
+ }
31
+ }
32
+ state.lastPrefix = prefix2;
33
+ let text = comment + parts.map((_, i) => `\0${accum.length + i}d\x7F`).join('') + wikitext.slice(total.length);
34
+ for (const part of parts) {
35
+ // @ts-expect-error abstract class
36
+ new list_1.ListToken(part, config, accum);
37
+ }
21
38
  if (!dt) {
22
39
  return text;
23
40
  }
@@ -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 magicLink_1 = require("../src/magicLink");
6
- const sepRegex = /[^,;\\.:!?)][,;\\.:!?)]+$/u, sepLparRegex = /[^,;\\.:!?][,;\\.:!?]+$/u;
7
6
  /**
8
7
  * 解析自由外链
9
8
  * @param wikitext
@@ -21,7 +20,7 @@ const parseMagicLinks = (wikitext, config, accum) => {
21
20
  trail = url.slice(m2.index);
22
21
  url = url.slice(0, m2.index);
23
22
  }
24
- const sep = url.includes('(') ? sepLparRegex : sepRegex, sepChars = sep.exec(url);
23
+ const sep = url.includes('(') ? /[^,;\\.:!?][,;\\.:!?]+$/u : /[^,;\\.:!?)][,;\\.:!?)]+$/u, sepChars = sep.exec(url);
25
24
  if (sepChars) {
26
25
  let correction = 1;
27
26
  if (sepChars[0][1] === ';'
@@ -67,8 +67,8 @@ const parseTable = ({ firstChild: { data }, type, name }, config, accum) => {
67
67
  out += `\n${outLine}`;
68
68
  continue;
69
69
  }
70
- // eslint-disable-next-line @stylistic/operator-linebreak
71
- const matches = /^(?:(\|\}|\0\d+!\x7F\}|\0\d+\}\x7F)|(\|-+|\0\d+!\x7F-+|\0\d+-\x7F-*)(?!-)|(!|(?:\||\0\d+!\x7F)\+?))(.*)$/u
70
+ const matches = // eslint-disable-line @stylistic/operator-linebreak
71
+ /^(?:(\|\}|\0\d+!\x7F\}|\0\d+\}\x7F)|(\|-+|\0\d+!\x7F-+|\0\d+-\x7F-*)(?!-)|(!|(?:\||\0\d+!\x7F)\+?))(.*)$/u
72
72
  .exec(line);
73
73
  if (!matches) {
74
74
  push(`\n${outLine}`, top);
package/dist/src/arg.js CHANGED
@@ -41,8 +41,8 @@ class ArgToken extends index_2.Token {
41
41
  }
42
42
  }
43
43
  /** @private */
44
- toString() {
45
- return `{{{${super.toString('|')}}}}`;
44
+ toString(skip) {
45
+ return `{{{${super.toString(skip, '|')}}}}`;
46
46
  }
47
47
  /** @private */
48
48
  text() {
@@ -62,10 +62,7 @@ class ArgToken extends index_2.Token {
62
62
  if (!this.getAttribute('include')) {
63
63
  const e = (0, lint_1.generateForSelf)(this, { start }, 'no-arg', 'unexpected template argument');
64
64
  if (argDefault) {
65
- e.fix = {
66
- range: [start, e.endIndex],
67
- text: argDefault.text(),
68
- };
65
+ e.fix = { range: [start, e.endIndex], text: argDefault.text() };
69
66
  }
70
67
  return [e];
71
68
  }
@@ -60,7 +60,6 @@ const commonHtmlAttrs = new Set([
60
60
  img: new Set(['alt', 'src', 'width', 'height', 'srcset']),
61
61
  font: new Set(['size', 'color', 'face']),
62
62
  hr: widthAttrs,
63
- rt: new Set(['rbspan']),
64
63
  data: new Set(['value']),
65
64
  time: new Set(['datetime']),
66
65
  meta: new Set(['itemprop', 'content']),
@@ -194,23 +193,15 @@ class AttributeToken extends index_2.Token {
194
193
  * @param quotes 引号
195
194
  */
196
195
  constructor(type, tag, key, equal = '', value, quotes = [], config = index_1.default.getConfig(), accum = []) {
197
- const keyToken = new atom_1.AtomToken(key, 'attr-key', config, accum, {});
196
+ const keyToken = new atom_1.AtomToken(key, 'attr-key', config, accum);
198
197
  let valueToken;
199
198
  if (key === 'title' || tag === 'img' && key === 'alt') {
200
199
  valueToken = new index_2.Token(value, config, accum, {});
201
200
  valueToken.type = 'attr-value';
202
201
  valueToken.setAttribute('stage', constants_1.MAX_STAGE - 1);
203
202
  }
204
- else if (tag === 'gallery' && key === 'caption') {
205
- const newConfig = {
206
- ...config,
207
- excludes: [...config.excludes, 'quote', 'extLink', 'magicLink', 'list'],
208
- };
209
- valueToken = new index_2.Token(value, newConfig, accum, {});
210
- valueToken.type = 'attr-value';
211
- valueToken.setAttribute('stage', 5);
212
- }
213
- else if (tag === 'choose' && (key === 'before' || key === 'after')) {
203
+ else if (tag === 'gallery' && key === 'caption'
204
+ || tag === 'choose' && (key === 'before' || key === 'after')) {
214
205
  const newConfig = {
215
206
  ...config,
216
207
  excludes: [...config.excludes, 'heading', 'html', 'table', 'hr', 'list'],
@@ -238,13 +229,13 @@ class AttributeToken extends index_2.Token {
238
229
  if (this.parentNode) {
239
230
  this.#tag = this.parentNode.name;
240
231
  }
241
- this.setAttribute('name', this.firstChild.text().trim().toLowerCase());
232
+ this.setAttribute('name', this.firstChild.toString(true).trim().toLowerCase());
242
233
  super.afterBuild();
243
234
  }
244
235
  /** @private */
245
- toString() {
236
+ toString(skip) {
246
237
  const [quoteStart = '', quoteEnd = ''] = this.#quotes;
247
- return this.#equal ? super.toString(this.#equal + quoteStart) + quoteEnd : this.firstChild.toString();
238
+ return this.#equal ? super.toString(skip, this.#equal + quoteStart) + quoteEnd : this.firstChild.toString(skip);
248
239
  }
249
240
  /** @private */
250
241
  text() {
@@ -261,10 +252,7 @@ class AttributeToken extends index_2.Token {
261
252
  const e = (0, lint_1.generateForChild)(lastChild, rect, 'unclosed-quote', index_1.default.msg('unclosed $1', 'quotes'), 'warning');
262
253
  e.startIndex--;
263
254
  e.startCol--;
264
- const fix = {
265
- range: [e.endIndex, e.endIndex],
266
- text: this.#quotes[0],
267
- };
255
+ const fix = { range: [e.endIndex, e.endIndex], text: this.#quotes[0] };
268
256
  if (lastChild.childNodes.some(({ type: t, data }) => t === 'text' && /\s/u.test(data))) {
269
257
  e.suggestions = [
270
258
  {
@@ -292,7 +280,7 @@ class AttributeToken extends index_2.Token {
292
280
  else if (name === 'style' && typeof value === 'string' && insecureStyle.test(value)) {
293
281
  errors.push((0, lint_1.generateForChild)(lastChild, rect, 'insecure-style', 'insecure style'));
294
282
  }
295
- else if (name === 'tabindex' && typeof value === 'string' && value.trim() !== '0') {
283
+ else if (name === 'tabindex' && typeof value === 'string' && value !== '0') {
296
284
  const e = (0, lint_1.generateForChild)(lastChild, rect, 'illegal-attr', 'nonzero tabindex');
297
285
  e.suggestions = [
298
286
  {
@@ -312,14 +300,7 @@ class AttributeToken extends index_2.Token {
312
300
  }
313
301
  /** 获取属性值 */
314
302
  getValue() {
315
- if (this.#equal) {
316
- const value = this.lastChild.text();
317
- if (this.#quotes[1]) {
318
- return value;
319
- }
320
- return value[this.#quotes[0] ? 'trimEnd' : 'trim']();
321
- }
322
- return this.type === 'ext-attr' || '';
303
+ return this.#equal ? this.lastChild.text().trim() : this.type === 'ext-attr' || '';
323
304
  }
324
305
  }
325
306
  exports.AttributeToken = AttributeToken;
@@ -8,7 +8,6 @@ const index_1 = require("../index");
8
8
  const index_2 = require("./index");
9
9
  const atom_1 = require("./atom");
10
10
  const attribute_1 = require("./attribute");
11
- const regex = /([^\s/](?:(?!\0\d+~\x7F)[^\s/=])*)(?:((?:\s(?:\s|\0\d+c\x7F)*)?(?:=|\0\d+~\x7F)(?:\s|\0\d+c\x7F)*)(?:(["'])(.*?)(\3|$)|(\S*)))?/gsu;
12
11
  /**
13
12
  * 将属性类型转换为单属性类型
14
13
  * @param type 属性类型
@@ -38,7 +37,7 @@ class AttributesToken extends index_2.Token {
38
37
  this.#type = type;
39
38
  this.setAttribute('name', name);
40
39
  if (attr) {
41
- regex.lastIndex = 0;
40
+ const regex = /([^\s/](?:(?!\0\d+~\x7F)[^\s/=])*)(?:((?:\s(?:\s|\0\d+c\x7F)*)?(?:=|\0\d+~\x7F)(?:\s|\0\d+c\x7F)*)(?:(["'])(.*?)(\3|$)|(\S*)))?/gsu;
42
41
  let out = '', mt = regex.exec(attr), lastIndex = 0;
43
42
  const insertDirty = /** 插入无效属性 */ () => {
44
43
  if (out) {
@@ -123,7 +122,7 @@ class AttributesToken extends index_2.Token {
123
122
  duplicated.add(name);
124
123
  attrs.get(name).push(attr);
125
124
  }
126
- else if (name !== 'class') {
125
+ else {
127
126
  attrs.set(name, [attr]);
128
127
  }
129
128
  }
@@ -7,7 +7,7 @@ import { ConverterRuleToken } from './converterRule';
7
7
  * @classdesc `{childNodes: [ConverterFlagsToken, ...ConverterRuleToken]}`
8
8
  */
9
9
  export declare abstract class ConverterToken extends Token {
10
- readonly childNodes: readonly [ConverterFlagsToken, ...ConverterRuleToken[]];
10
+ readonly childNodes: readonly [ConverterFlagsToken, ConverterRuleToken, ...ConverterRuleToken[]];
11
11
  abstract get firstChild(): ConverterFlagsToken;
12
12
  abstract get lastChild(): ConverterFlagsToken | ConverterRuleToken;
13
13
  get type(): 'converter';
@@ -25,7 +25,8 @@ class ConverterToken extends index_2.Token {
25
25
  const [firstRule] = rules, hasColon = firstRule.includes(':'),
26
26
  // @ts-expect-error abstract class
27
27
  firstRuleToken = new converterRule_1.ConverterRuleToken(firstRule, hasColon, config, accum);
28
- if (hasColon && firstRuleToken.length === 1) {
28
+ if (hasColon && firstRuleToken.length === 1
29
+ || !hasColon && rules.length === 2 && !(0, string_1.removeComment)(rules[1]).trim()) {
29
30
  // @ts-expect-error abstract class
30
31
  this.insertAt(new converterRule_1.ConverterRuleToken(rules.join(';'), false, config, accum));
31
32
  }
@@ -36,9 +37,9 @@ class ConverterToken extends index_2.Token {
36
37
  }
37
38
  }
38
39
  /** @private */
39
- toString() {
40
+ toString(skip) {
40
41
  const { childNodes: [flags, ...rules] } = this;
41
- return `-{${flags.toString()}${flags.length > 0 ? '|' : ''}${rules.map(String).join(';')}}-`;
42
+ return `-{${flags.toString(skip)}${flags.length > 0 ? '|' : ''}${rules.map(rule => rule.toString(skip)).join(';')}}-`;
42
43
  }
43
44
  /** @private */
44
45
  text() {
@@ -27,8 +27,8 @@ class ConverterFlagsToken extends index_2.Token {
27
27
  super.afterBuild();
28
28
  }
29
29
  /** @private */
30
- toString() {
31
- return super.toString(';');
30
+ toString(skip) {
31
+ return super.toString(skip, ';');
32
32
  }
33
33
  /** @private */
34
34
  text() {
@@ -62,16 +62,13 @@ class ConverterFlagsToken extends index_2.Token {
62
62
  && (variantFlags.size > 0 || !validFlags.has(flag))) {
63
63
  const e = (0, lint_1.generateForChild)(child, rect, 'no-ignored', 'invalid conversion flag');
64
64
  if (variantFlags.size === 0 && definedFlags.has(flag.toUpperCase())) {
65
- e.fix = {
66
- range: [e.startIndex, e.endIndex],
67
- text: flag.toUpperCase(),
68
- };
65
+ e.fix = { range: [e.startIndex, e.endIndex], text: flag.toUpperCase() };
69
66
  }
70
67
  else {
71
68
  e.suggestions = [
72
69
  {
73
70
  desc: 'remove',
74
- range: [e.startIndex, e.endIndex],
71
+ range: [e.startIndex - (i && 1), e.endIndex],
75
72
  text: '',
76
73
  },
77
74
  ];
@@ -31,22 +31,18 @@ class ConverterRuleToken extends index_2.Token {
31
31
  }
32
32
  }
33
33
  /** @private */
34
- toString() {
35
- const { childNodes } = this;
36
- if (childNodes.length === 3) {
37
- const [from, variant, to] = childNodes;
38
- return `${from.toString()}=>${variant.toString()}:${to.toString()}`;
39
- }
40
- return super.toString(':');
34
+ toString(skip) {
35
+ const { childNodes, firstChild, lastChild } = this;
36
+ return childNodes.length === 3
37
+ ? `${firstChild.toString(skip)}=>${childNodes[1].toString(skip)}:${lastChild.toString(skip)}`
38
+ : super.toString(skip, ':');
41
39
  }
42
40
  /** @private */
43
41
  text() {
44
- const { childNodes } = this;
45
- if (childNodes.length === 3) {
46
- const [from, variant, to] = childNodes;
47
- return `${from.text()}=>${variant.text()}:${to.text()}`;
48
- }
49
- return super.text(':');
42
+ const { childNodes, firstChild, lastChild } = this;
43
+ return childNodes.length === 3
44
+ ? `${firstChild.text()}=>${childNodes[1].text()}:${lastChild.text()}`
45
+ : super.text(':');
50
46
  }
51
47
  /** @private */
52
48
  getGaps(i) {