wikilint 2.23.0 → 2.25.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 (77) hide show
  1. package/README.md +3 -3
  2. package/bin/config.js +2 -2
  3. package/config/default.json +6 -0
  4. package/config/enwiki.json +2 -2
  5. package/config/llwiki.json +382 -261
  6. package/config/moegirl.json +325 -325
  7. package/coverage/badge.svg +1 -1
  8. package/dist/base.d.mts +20 -2
  9. package/dist/base.d.ts +20 -2
  10. package/dist/bin/config.js +17 -5
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.js +32 -12
  13. package/dist/lib/element.d.ts +0 -7
  14. package/dist/lib/element.js +7 -13
  15. package/dist/lib/lintConfig.js +0 -3
  16. package/dist/lib/lsp.d.ts +19 -1
  17. package/dist/lib/lsp.js +130 -15
  18. package/dist/lib/text.d.ts +17 -0
  19. package/dist/lib/text.js +73 -11
  20. package/dist/lib/title.js +3 -2
  21. package/dist/mixin/attributesParent.js +1 -1
  22. package/dist/mixin/elementLike.d.ts +7 -0
  23. package/dist/mixin/elementLike.js +7 -0
  24. package/dist/mixin/noEscape.d.ts +4 -0
  25. package/dist/mixin/noEscape.js +20 -0
  26. package/dist/parser/commentAndExt.js +7 -8
  27. package/dist/parser/externalLinks.js +1 -1
  28. package/dist/parser/hrAndDoubleUnderscore.js +8 -5
  29. package/dist/src/arg.js +7 -6
  30. package/dist/src/attribute.d.ts +1 -0
  31. package/dist/src/attribute.js +18 -13
  32. package/dist/src/attributes.js +6 -6
  33. package/dist/src/converter.js +2 -1
  34. package/dist/src/converterFlags.js +3 -3
  35. package/dist/src/extLink.js +1 -1
  36. package/dist/src/gallery.js +9 -8
  37. package/dist/src/heading.js +158 -98
  38. package/dist/src/html.js +20 -18
  39. package/dist/src/imageParameter.js +4 -4
  40. package/dist/src/imagemap.js +4 -4
  41. package/dist/src/index.d.ts +4 -0
  42. package/dist/src/index.js +2 -2
  43. package/dist/src/link/base.js +9 -22
  44. package/dist/src/link/file.js +18 -8
  45. package/dist/src/link/galleryImage.js +2 -2
  46. package/dist/src/link/index.d.ts +2 -0
  47. package/dist/src/link/index.js +10 -1
  48. package/dist/src/link/redirectTarget.js +2 -2
  49. package/dist/src/magicLink.js +5 -5
  50. package/dist/src/nested.js +3 -3
  51. package/dist/src/nowiki/base.js +61 -10
  52. package/dist/src/nowiki/comment.js +2 -2
  53. package/dist/src/nowiki/index.js +3 -3
  54. package/dist/src/nowiki/quote.js +11 -11
  55. package/dist/src/onlyinclude.js +2 -1
  56. package/dist/src/paramTag/index.js +2 -2
  57. package/dist/src/parameter.js +2 -2
  58. package/dist/src/redirect.js +2 -1
  59. package/dist/src/table/base.d.ts +2 -0
  60. package/dist/src/table/base.js +30 -2
  61. package/dist/src/table/index.js +4 -5
  62. package/dist/src/table/td.d.ts +1 -0
  63. package/dist/src/table/td.js +14 -13
  64. package/dist/src/table/trBase.js +1 -1
  65. package/dist/src/tagPair/ext.js +2 -2
  66. package/dist/src/tagPair/include.js +4 -4
  67. package/dist/src/tagPair/index.js +2 -1
  68. package/dist/src/transclude.js +13 -18
  69. package/dist/util/constants.js +2 -1
  70. package/dist/util/lint.js +91 -2
  71. package/dist/util/sharable.js +1 -1
  72. package/dist/util/sharable.mjs +1 -1
  73. package/dist/util/string.js +11 -10
  74. package/i18n/en.json +77 -0
  75. package/i18n/zh-hans.json +71 -56
  76. package/i18n/zh-hant.json +71 -56
  77. package/package.json +8 -9
@@ -1,4 +1,38 @@
1
1
  "use strict";
2
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
3
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
4
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
5
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
6
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
7
+ var _, done = false;
8
+ for (var i = decorators.length - 1; i >= 0; i--) {
9
+ var context = {};
10
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
11
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
12
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
13
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
14
+ if (kind === "accessor") {
15
+ if (result === void 0) continue;
16
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
17
+ if (_ = accept(result.get)) descriptor.get = _;
18
+ if (_ = accept(result.set)) descriptor.set = _;
19
+ if (_ = accept(result.init)) initializers.unshift(_);
20
+ }
21
+ else if (_ = accept(result)) {
22
+ if (kind === "field") initializers.unshift(_);
23
+ else descriptor[key] = _;
24
+ }
25
+ }
26
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
27
+ done = true;
28
+ };
29
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
30
+ var useValue = arguments.length > 2;
31
+ for (var i = 0; i < initializers.length; i++) {
32
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
33
+ }
34
+ return useValue ? value : void 0;
35
+ };
2
36
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
38
  };
@@ -7,6 +41,7 @@ exports.HeadingToken = void 0;
7
41
  const lint_1 = require("../util/lint");
8
42
  const debug_1 = require("../util/debug");
9
43
  const rect_1 = require("../lib/rect");
44
+ const noEscape_1 = require("../mixin/noEscape");
10
45
  const index_1 = __importDefault(require("../index"));
11
46
  const index_2 = require("./index");
12
47
  const syntax_1 = require("./syntax");
@@ -16,115 +51,140 @@ const syntax_1 = require("./syntax");
16
51
  * 章节标题
17
52
  * @classdesc `{childNodes: [Token, SyntaxToken]}`
18
53
  */
19
- class HeadingToken extends index_2.Token {
20
- #level;
21
- get type() {
22
- return 'heading';
23
- }
24
- /** level of the heading / 标题层级 */
25
- get level() {
26
- return this.#level;
27
- }
28
- /**
29
- * @param level 标题层级
30
- * @param input 标题文字
31
- */
32
- constructor(level, input, config, accum = []) {
33
- super(undefined, config, accum);
34
- this.#level = level;
35
- const token = new index_2.Token(input[0], config, accum);
36
- token.type = 'heading-title';
37
- token.setAttribute('stage', 2);
38
- const trail = new syntax_1.SyntaxToken(input[1], 'heading-trail', config, accum);
39
- this.append(token, trail);
40
- }
41
- /** 标题格式的等号 */
42
- #getEquals() {
43
- return '='.repeat(this.level);
44
- }
45
- /** @private */
46
- toString(skip) {
47
- const equals = this.#getEquals();
48
- return equals + this.firstChild.toString(skip) + equals + this.lastChild.toString(skip);
49
- }
50
- /** @private */
51
- text() {
52
- const equals = this.#getEquals();
53
- return equals + this.firstChild.text() + equals;
54
- }
55
- /** @private */
56
- getAttribute(key) {
57
- return key === 'padding' ? this.level : super.getAttribute(key);
58
- }
59
- /** @private */
60
- getGaps() {
61
- return this.level;
62
- }
63
- /** @private */
64
- lint(start = this.getAbsoluteIndex(), re) {
65
- const errors = super.lint(start, re), { firstChild, level } = this, innerStr = firstChild.toString(), unbalancedStart = innerStr.startsWith('='), unbalanced = unbalancedStart || innerStr.endsWith('='), rect = new rect_1.BoundingRect(this, start), s = this.inHtmlAttrs(), rules = ['h1', 'unbalanced-header', 'format-leakage'], severities = rules.map(rule => index_1.default.lintConfig.getSeverity(rule, 'apostrophe'));
66
- if (severities[0] && this.level === 1) {
67
- const e = (0, lint_1.generateForChild)(firstChild, rect, rules[0], '<h1>', severities[0]);
68
- if (!unbalanced) {
69
- e.suggestions = [{ desc: 'h2', range: [e.startIndex, e.endIndex], text: `=${innerStr}=` }];
70
- }
71
- errors.push(e);
54
+ let HeadingToken = (() => {
55
+ let _classDecorators = [noEscape_1.noEscape];
56
+ let _classDescriptor;
57
+ let _classExtraInitializers = [];
58
+ let _classThis;
59
+ let _classSuper = index_2.Token;
60
+ var HeadingToken = class extends _classSuper {
61
+ static { _classThis = this; }
62
+ static {
63
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
64
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
65
+ HeadingToken = _classThis = _classDescriptor.value;
66
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
67
+ __runInitializers(_classThis, _classExtraInitializers);
72
68
  }
73
- if (severities[1] && unbalanced) {
74
- const msg = index_1.default.msg('unbalanced $1 in a section header', '"="'), e = (0, lint_1.generateForChild)(firstChild, rect, rules[1], msg, severities[1]);
75
- if (innerStr === '=') {
76
- //
77
- }
78
- else if (unbalancedStart) {
79
- const [extra] = /^=+/u.exec(innerStr), newLevel = level + extra.length;
80
- e.suggestions = [{ desc: `h${level}`, range: [e.startIndex, e.startIndex + extra.length], text: '' }];
81
- if (newLevel < 7) {
82
- e.suggestions.push({ desc: `h${newLevel}`, range: [e.endIndex, e.endIndex], text: extra });
69
+ #level;
70
+ get type() {
71
+ return 'heading';
72
+ }
73
+ /** level of the heading / 标题层级 */
74
+ get level() {
75
+ return this.#level;
76
+ }
77
+ /**
78
+ * @param level 标题层级
79
+ * @param input 标题文字
80
+ */
81
+ constructor(level, input, config, accum = []) {
82
+ super(undefined, config, accum);
83
+ this.#level = level;
84
+ const token = new index_2.Token(input[0], config, accum);
85
+ token.type = 'heading-title';
86
+ token.setAttribute('stage', 2);
87
+ const trail = new syntax_1.SyntaxToken(input[1], 'heading-trail', config, accum);
88
+ this.append(token, trail);
89
+ }
90
+ /** 标题格式的等号 */
91
+ #getEquals() {
92
+ return '='.repeat(this.level);
93
+ }
94
+ /** @private */
95
+ toString(skip) {
96
+ const equals = this.#getEquals();
97
+ return equals + this.firstChild.toString(skip) + equals + this.lastChild.toString(skip);
98
+ }
99
+ /** @private */
100
+ text() {
101
+ const equals = this.#getEquals();
102
+ return equals + this.firstChild.text() + equals;
103
+ }
104
+ /** @private */
105
+ getAttribute(key) {
106
+ return key === 'padding' ? this.level : super.getAttribute(key);
107
+ }
108
+ /** @private */
109
+ getGaps() {
110
+ return this.level;
111
+ }
112
+ /** @private */
113
+ lint(start = this.getAbsoluteIndex(), re) {
114
+ const errors = super.lint(start, re), { firstChild, level } = this, innerStr = firstChild.toString(), unbalancedStart = innerStr.startsWith('='), unbalanced = unbalancedStart || innerStr.endsWith('='), rect = new rect_1.BoundingRect(this, start), s = this.inHtmlAttrs(), rules = ['h1', 'unbalanced-header', 'format-leakage'], severities = rules.map(rule => index_1.default.lintConfig.getSeverity(rule, 'apostrophe'));
115
+ if (severities[0] && this.level === 1) {
116
+ const e = (0, lint_1.generateForChild)(firstChild, rect, rules[0], '<h1>', severities[0]);
117
+ if (!unbalanced) {
118
+ e.suggestions = [(0, lint_1.fixBy)(e, 'h2', `=${innerStr}=`)];
83
119
  }
120
+ errors.push(e);
84
121
  }
85
- else {
86
- const extra = /[^=](=+)$/u.exec(innerStr)[1], newLevel = level + extra.length;
87
- e.suggestions = [{ desc: `h${level}`, range: [e.endIndex - extra.length, e.endIndex], text: '' }];
88
- if (newLevel < 7) {
89
- e.suggestions.push({ desc: `h${newLevel}`, range: [e.startIndex, e.startIndex], text: extra });
122
+ if (severities[1] && unbalanced) {
123
+ const msg = index_1.default.msg('unbalanced-in-section-header', '"="'), e = (0, lint_1.generateForChild)(firstChild, rect, rules[1], msg, severities[1]);
124
+ if (innerStr === '=') {
125
+ //
90
126
  }
91
- }
92
- errors.push(e);
93
- }
94
- if (s) {
95
- const rule = 'parsing-order', severity = index_1.default.lintConfig.getSeverity(rule, s === 2 ? 'heading' : 'templateInTable');
96
- if (severity) {
97
- errors.push((0, lint_1.generateForSelf)(this, rect, rule, 'section header in HTML tag attributes', severity));
98
- }
99
- }
100
- if (severities[2]) {
101
- const rootStr = this.getRootNode().toString(), quotes = firstChild.childNodes.filter((0, debug_1.isToken)('quote')), boldQuotes = quotes.filter(({ bold }) => bold), italicQuotes = quotes.filter(({ italic }) => italic);
102
- if (boldQuotes.length % 2) {
103
- const e = (0, lint_1.generateForChild)(boldQuotes[boldQuotes.length - 1], {
104
- ...rect, // eslint-disable-line @typescript-eslint/no-misused-spread
105
- start: start + level,
106
- left: rect.left + level,
107
- }, rules[2], index_1.default.msg('unbalanced $1 in a section header', 'bold apostrophes'), severities[2]), end = start + level + innerStr.length;
108
- if (rootStr.slice(e.endIndex, end).trim()) {
109
- e.suggestions = [{ desc: 'close', range: [end, end], text: `'''` }];
127
+ else if (unbalancedStart) {
128
+ const [extra] = /^=+/u.exec(innerStr), newLevel = level + extra.length;
129
+ e.suggestions = [{ desc: `h${level}`, range: [e.startIndex, e.startIndex + extra.length], text: '' }];
130
+ if (newLevel < 7) {
131
+ e.suggestions.push({ desc: `h${newLevel}`, range: [e.endIndex, e.endIndex], text: extra });
132
+ }
110
133
  }
111
134
  else {
112
- e.fix = { desc: 'remove', range: [e.startIndex, e.endIndex], text: '' };
135
+ const extra = /[^=](=+)$/u.exec(innerStr)[1], newLevel = level + extra.length;
136
+ e.suggestions = [{ desc: `h${level}`, range: [e.endIndex - extra.length, e.endIndex], text: '' }];
137
+ if (newLevel < 7) {
138
+ e.suggestions.push({ desc: `h${newLevel}`, range: [e.startIndex, e.startIndex], text: extra });
139
+ }
113
140
  }
114
141
  errors.push(e);
115
142
  }
116
- if (italicQuotes.length % 2) {
117
- const e = (0, lint_1.generateForChild)(italicQuotes[italicQuotes.length - 1], { start: start + level }, rules[2], index_1.default.msg('unbalanced $1 in a section header', 'italic apostrophes'), severities[2]), end = start + level + innerStr.length;
118
- if (rootStr.slice(e.endIndex, end).trim()) {
119
- e.suggestions = [{ desc: 'close', range: [end, end], text: `''` }];
143
+ if (s) {
144
+ const rule = 'parsing-order', severity = index_1.default.lintConfig.getSeverity(rule, s === 2 ? 'heading' : 'templateInTable');
145
+ if (severity) {
146
+ errors.push((0, lint_1.generateForSelf)(this, rect, rule, 'header-in-html', severity));
120
147
  }
121
- else {
122
- e.fix = { desc: 'remove', range: [e.startIndex, e.endIndex], text: '' };
148
+ }
149
+ if (severities[2]) {
150
+ const rootStr = this.getRootNode().toString(), quotes = firstChild.childNodes.filter((0, debug_1.isToken)('quote')), boldQuotes = quotes.filter(({ bold }) => bold), italicQuotes = quotes.filter(({ italic }) => italic);
151
+ if (boldQuotes.length % 2) {
152
+ const e = (0, lint_1.generateForChild)(boldQuotes[boldQuotes.length - 1], {
153
+ ...rect, // eslint-disable-line @typescript-eslint/no-misused-spread
154
+ start: start + level,
155
+ left: rect.left + level,
156
+ }, rules[2], index_1.default.msg('unbalanced-in-section-header', 'bold-apostrophes'), severities[2]), end = start + level + innerStr.length, remove = (0, lint_1.fixByRemove)(e);
157
+ if (rootStr.slice(e.endIndex, end).trim()) {
158
+ e.suggestions = [
159
+ remove,
160
+ (0, lint_1.fixByClose)(end, `'''`),
161
+ ];
162
+ }
163
+ else if (boldQuotes.length === 1 && italicQuotes.length === 0) {
164
+ e.fix = remove;
165
+ }
166
+ else {
167
+ e.suggestions = [remove];
168
+ }
169
+ errors.push(e);
170
+ }
171
+ if (italicQuotes.length % 2) {
172
+ const e = (0, lint_1.generateForChild)(italicQuotes[italicQuotes.length - 1], { start: start + level }, rules[2], index_1.default.msg('unbalanced-in-section-header', 'italic-apostrophes'), severities[2]), end = start + level + innerStr.length;
173
+ if (rootStr.slice(e.endIndex, end).trim()) {
174
+ e.suggestions = [(0, lint_1.fixByClose)(end, `''`)];
175
+ }
176
+ else if (italicQuotes.length === 1 && boldQuotes.length === 0) {
177
+ e.fix = (0, lint_1.fixByRemove)(e);
178
+ }
179
+ else {
180
+ e.suggestions = [(0, lint_1.fixByRemove)(e)];
181
+ }
182
+ errors.push(e);
123
183
  }
124
- errors.push(e);
125
184
  }
185
+ return errors;
126
186
  }
127
- return errors;
128
- }
129
- }
187
+ };
188
+ return HeadingToken = _classThis;
189
+ })();
130
190
  exports.HeadingToken = HeadingToken;
package/dist/src/html.js CHANGED
@@ -148,63 +148,65 @@ let HtmlToken = (() => {
148
148
  rule = 'parsing-order';
149
149
  s = severity && index_1.default.lintConfig.getSeverity(rule, severity === 2 ? 'html' : 'templateInTable');
150
150
  if (s) {
151
- const e = (0, lint_1.generateForSelf)(this, rect, rule, 'HTML tag in table attributes', s);
151
+ const e = (0, lint_1.generateForSelf)(this, rect, rule, 'html-in-table', s);
152
152
  if (severity === 2) {
153
- e.fix = { desc: 'remove', range: [start, e.endIndex], text: '' };
153
+ e.suggestions = [(0, lint_1.fixByRemove)(e)];
154
154
  }
155
155
  errors.push(e);
156
156
  }
157
157
  rule = 'obsolete-tag';
158
158
  s = index_1.default.lintConfig.getSeverity(rule, name);
159
159
  if (s && obsoleteTags.has(name)) {
160
- errors.push((0, lint_1.generateForSelf)(this, rect, rule, 'obsolete HTML tag', s));
160
+ errors.push((0, lint_1.generateForSelf)(this, rect, rule, 'obsolete-tag', s));
161
161
  }
162
162
  rule = 'bold-header';
163
163
  s = index_1.default.lintConfig.getSeverity(rule, name);
164
- if (s && (name === 'b' || name === 'strong') && this.closest('heading-title')) {
165
- errors.push((0, lint_1.generateForSelf)(this, rect, rule, 'bold in section header', s));
164
+ if (s && (name === 'b' || name === 'strong')
165
+ && this.closest('heading-title,ext')?.type === 'heading-title') {
166
+ const e = (0, lint_1.generateForSelf)(this, rect, rule, 'bold-in-header', s);
167
+ e.suggestions = [(0, lint_1.fixByRemove)(e)];
168
+ errors.push(e);
166
169
  }
167
170
  const { html: [, flexibleTags, voidTags] } = this.getAttribute('config'), isVoid = voidTags.includes(name), isFlexible = flexibleTags.includes(name), isNormal = !isVoid && !isFlexible;
168
171
  rule = 'unmatched-tag';
169
172
  if (closing && (selfClosing || isVoid) || selfClosing && isNormal) {
170
173
  s = index_1.default.lintConfig.getSeverity(rule, closing ? 'both' : 'selfClosing');
171
174
  if (s) {
172
- const error = (0, lint_1.generateForSelf)(this, rect, rule, closing ? 'tag that is both closing and self-closing' : 'invalid self-closing tag', s), open = { desc: 'open', range: [start + 1, start + 2], text: '' }, noSelfClosing = {
173
- desc: 'no self-closing',
174
- range: [error.endIndex - 2, error.endIndex - 1],
175
+ const e = (0, lint_1.generateForSelf)(this, rect, rule, closing ? 'closing-and-self-closing' : 'invalid-self-closing', s), open = (0, lint_1.fixByOpen)(start), noSelfClosing = {
176
+ desc: index_1.default.msg('no-self-closing'),
177
+ range: [e.endIndex - 2, e.endIndex - 1],
175
178
  text: '',
176
179
  };
177
180
  if (isFlexible) {
178
- error.suggestions = [open, noSelfClosing];
181
+ e.suggestions = [open, noSelfClosing];
179
182
  }
180
183
  else if (closing) {
181
- error.fix = isVoid ? open : noSelfClosing;
184
+ e.fix = isVoid ? open : noSelfClosing;
182
185
  }
183
186
  else {
184
- error.suggestions = [
187
+ e.suggestions = [
185
188
  noSelfClosing,
186
- { desc: 'close', range: [error.endIndex - 2, error.endIndex], text: `></${name}>` },
189
+ (0, lint_1.fixByClose)(e.endIndex, `></${name}>`, -2),
187
190
  ];
188
191
  }
189
- errors.push(error);
192
+ errors.push(e);
190
193
  }
191
194
  }
192
195
  else if (!this.findMatchingTag()) {
193
- const error = (0, lint_1.generateForSelf)(this, rect, rule, closing ? 'unmatched closing tag' : 'unclosed tag'), ancestor = this.closest('magic-word');
196
+ const error = (0, lint_1.generateForSelf)(this, rect, rule, closing ? 'unmatched-closing' : 'unclosed-tag'), ancestor = this.closest('magic-word');
194
197
  if (ancestor && magicWords.has(ancestor.name)) {
195
198
  s = index_1.default.lintConfig.getSeverity(rule, 'conditional');
196
199
  }
197
200
  else if (closing) {
198
201
  s = index_1.default.lintConfig.getSeverity(rule, 'closing');
199
- error.suggestions = [{ desc: 'remove', range: [start, error.endIndex], text: '' }];
202
+ error.suggestions = [(0, lint_1.fixByRemove)(error)];
200
203
  }
201
204
  else {
202
205
  s = index_1.default.lintConfig.getSeverity(rule, 'opening');
203
206
  const childNodes = parentNode?.childNodes;
204
207
  if (formattingTags.has(name)) {
205
- if (childNodes?.slice(0, childNodes.indexOf(this))
206
- .some(({ type, name: n }) => type === 'html' && n === name)) {
207
- error.suggestions = [{ desc: 'close', range: [start + 1, start + 1], text: '/' }];
208
+ if (childNodes?.slice(0, childNodes.indexOf(this)).some(tag => tag.type === 'html' && tag.name === name && !tag.findMatchingTag())) {
209
+ error.suggestions = [(0, lint_1.fixByClose)(start + 1, '/')];
208
210
  }
209
211
  if (this.closest('heading-title')) {
210
212
  error.rule = 'format-leakage';
@@ -122,16 +122,16 @@ class ImageParameterToken extends index_2.Token {
122
122
  if (name === 'invalid') {
123
123
  const rule = 'invalid-gallery', s = index_1.default.lintConfig.getSeverity(rule, 'parameter');
124
124
  if (s) {
125
- const e = (0, lint_1.generateForSelf)(this, { start }, rule, 'invalid image parameter', s);
126
- e.fix = { desc: 'remove', range: [start - 1, e.endIndex], text: '' };
125
+ const e = (0, lint_1.generateForSelf)(this, { start }, rule, 'invalid-image-parameter', s);
126
+ e.fix = (0, lint_1.fixByRemove)(e, -1);
127
127
  errors.push(e);
128
128
  }
129
129
  }
130
130
  else if (typeof link === 'object' && link.encoded) {
131
131
  const rule = 'url-encoding', s = index_1.default.lintConfig.getSeverity(rule, 'file');
132
132
  if (s) {
133
- const e = (0, lint_1.generateForSelf)(this, { start }, rule, 'unnecessary URL encoding in an internal link', s);
134
- e.suggestions = [{ desc: 'decode', range: [start, e.endIndex], text: (0, string_1.rawurldecode)(this.text()) }];
133
+ const e = (0, lint_1.generateForSelf)(this, { start }, rule, 'unnecessary-encoding', s);
134
+ e.fix = (0, lint_1.fixByDecode)(e, this);
135
135
  errors.push(e);
136
136
  }
137
137
  }
@@ -140,16 +140,16 @@ let ImagemapToken = (() => {
140
140
  const str = child.toString().trim();
141
141
  return child.is('noinclude') && str && !str.startsWith('#');
142
142
  }).map(child => {
143
- const e = (0, lint_1.generateForChild)(child, rect, rule, 'invalid link in <imagemap>', s);
143
+ const e = (0, lint_1.generateForChild)(child, rect, rule, 'invalid-imagemap-link', s);
144
144
  e.suggestions = [
145
- { desc: 'remove', range: [e.startIndex - 1, e.endIndex], text: '' },
146
- { desc: 'comment', range: [e.startIndex, e.startIndex], text: '# ' },
145
+ (0, lint_1.fixByRemove)(e, -1),
146
+ (0, lint_1.fixBy)(e, 'comment', '# '),
147
147
  ];
148
148
  return e;
149
149
  }));
150
150
  }
151
151
  else {
152
- errors.push((0, lint_1.generateForSelf)(this, rect, rule, '<imagemap> without an image', s));
152
+ errors.push((0, lint_1.generateForSelf)(this, rect, rule, 'imagemap-without-image', s));
153
153
  }
154
154
  }
155
155
  return errors;
@@ -4,6 +4,9 @@ import { AstText } from '../lib/text';
4
4
  import type { LintError, TokenTypes } from '../base';
5
5
  import type { Title, TitleOptions } from '../lib/title';
6
6
  import type { AstNodes } from '../internal';
7
+ declare type ExtendedLintError = LintError[] & {
8
+ output?: string;
9
+ };
7
10
  /**
8
11
  * base class for all tokens
9
12
  *
@@ -24,3 +27,4 @@ export declare class Token extends AstElement {
24
27
  insertAt(child: string, i?: number): AstText;
25
28
  insertAt<T extends AstNodes>(child: T, i?: number): T;
26
29
  }
30
+ export {};
package/dist/src/index.js CHANGED
@@ -396,11 +396,11 @@ class Token extends element_1.AstElement {
396
396
  }
397
397
  for (const [key, value] of record) {
398
398
  if (value.size > 1 && !key.startsWith('#mw-customcollapsible-')) {
399
- const isCat = !key.startsWith('#'), msg = `duplicated ${isCat ? 'category' : 'id'}`, severity = s[isCat ? 0 : 1];
399
+ const isCat = !key.startsWith('#'), msg = `duplicate-${isCat ? 'category' : 'id'}`, severity = s[isCat ? 0 : 1];
400
400
  errors.push(...[...value].map(cat => {
401
401
  const e = (0, lint_1.generateForSelf)(cat, { start: cat.getAbsoluteIndex() }, r, msg, severity);
402
402
  if (isCat) {
403
- e.suggestions = [{ desc: 'remove', range: [e.startIndex, e.endIndex], text: '' }];
403
+ e.suggestions = [(0, lint_1.fixByRemove)(e)];
404
404
  }
405
405
  return e;
406
406
  }));
@@ -40,9 +40,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.LinkBaseToken = void 0;
41
41
  const lint_1 = require("../../util/lint");
42
42
  const constants_1 = require("../../util/constants");
43
- const string_1 = require("../../util/string");
44
43
  const rect_1 = require("../../lib/rect");
45
44
  const padded_1 = require("../../mixin/padded");
45
+ const noEscape_1 = require("../../mixin/noEscape");
46
46
  const index_1 = __importDefault(require("../../index"));
47
47
  const index_2 = require("../index");
48
48
  const atom_1 = require("../atom");
@@ -58,7 +58,7 @@ const isLink = (type) => type === 'redirect-target' || type === 'link';
58
58
  * @classdesc `{childNodes: [AtomToken, ...Token[]]}`
59
59
  */
60
60
  let LinkBaseToken = (() => {
61
- let _classDecorators = [(0, padded_1.padded)('[[')];
61
+ let _classDecorators = [noEscape_1.noEscape, (0, padded_1.padded)('[[')];
62
62
  let _classDescriptor;
63
63
  let _classExtraInitializers = [];
64
64
  let _classThis;
@@ -140,13 +140,13 @@ let LinkBaseToken = (() => {
140
140
  const errors = super.lint(start, re), { childNodes: [target, linkText], type } = this, { encoded, fragment } = this.#title, rect = new rect_1.BoundingRect(this, start);
141
141
  let rule = 'unknown-page', s = index_1.default.lintConfig.getSeverity(rule);
142
142
  if (s && target.childNodes.some(({ type: t }) => t === 'template')) {
143
- errors.push((0, lint_1.generateForChild)(target, rect, rule, 'template in an internal link target', s));
143
+ errors.push((0, lint_1.generateForChild)(target, rect, rule, 'template-in-link', s));
144
144
  }
145
145
  rule = 'url-encoding';
146
146
  s = index_1.default.lintConfig.getSeverity(rule);
147
147
  if (s && encoded) {
148
- const e = (0, lint_1.generateForChild)(target, rect, rule, 'unnecessary URL encoding in an internal link', s);
149
- e.fix = { desc: 'decode', range: [e.startIndex, e.endIndex], text: (0, string_1.rawurldecode)(target.text()) };
148
+ const e = (0, lint_1.generateForChild)(target, rect, rule, 'unnecessary-encoding', s);
149
+ e.fix = (0, lint_1.fixByDecode)(e, target);
150
150
  errors.push(e);
151
151
  }
152
152
  rule = 'pipe-like';
@@ -154,30 +154,17 @@ let LinkBaseToken = (() => {
154
154
  if (s && (type === 'link' || type === 'category')) {
155
155
  const j = linkText?.childNodes.findIndex(c => c.type === 'text' && c.data.includes('|')), textNode = linkText?.childNodes[j];
156
156
  if (textNode) {
157
- const e = (0, lint_1.generateForChild)(linkText, rect, rule, 'additional "|" in the link text', s), i = e.startIndex + linkText.getRelativeIndex(j);
158
- e.suggestions = [
159
- {
160
- desc: 'escape',
161
- range: [i, i + textNode.data.length],
162
- text: textNode.data.replace(/\|/gu, '&#124;'),
163
- },
164
- ];
157
+ const e = (0, lint_1.generateForChild)(linkText, rect, rule, 'pipe-in-link', s), i = e.startIndex + linkText.getRelativeIndex(j);
158
+ e.suggestions = [(0, lint_1.fixByPipe)(i, textNode.data)];
165
159
  errors.push(e);
166
160
  }
167
161
  }
168
162
  rule = 'no-ignored';
169
163
  s = index_1.default.lintConfig.getSeverity(rule, 'fragment');
170
164
  if (s && fragment !== undefined && !isLink(type)) {
171
- const e = (0, lint_1.generateForChild)(target, rect, rule, 'useless fragment', s), j = target.childNodes.findIndex(c => c.type === 'text' && c.data.includes('#')), textNode = target.childNodes[j];
165
+ const e = (0, lint_1.generateForChild)(target, rect, rule, 'useless-fragment', s), j = target.childNodes.findIndex(c => c.type === 'text' && c.data.includes('#')), textNode = target.childNodes[j];
172
166
  if (textNode) {
173
- e.fix = {
174
- desc: 'remove',
175
- range: [
176
- e.startIndex + target.getRelativeIndex(j) + textNode.data.indexOf('#'),
177
- e.endIndex,
178
- ],
179
- text: '',
180
- };
167
+ e.fix = (0, lint_1.fixByRemove)(e, target.getRelativeIndex(j) + textNode.data.indexOf('#'));
181
168
  }
182
169
  errors.push(e);
183
170
  }
@@ -81,19 +81,28 @@ class FileToken extends base_1.LinkBaseToken {
81
81
  }
82
82
  /** @private */
83
83
  lint(start = this.getAbsoluteIndex(), re) {
84
- const errors = super.lint(start, re), args = filterArgs(this.getAllArgs(), argTypes), keys = [...new Set(args.map(({ name }) => name))], frameKeys = keys.filter(key => frame.has(key)), horizAlignKeys = keys.filter(key => horizAlign.has(key)), vertAlignKeys = keys.filter(key => vertAlign.has(key)), [fr] = frameKeys, unscaled = fr === 'framed' || fr === 'manualthumb', rect = new rect_1.BoundingRect(this, start);
84
+ const errors = super.lint(start, re), args = filterArgs(this.getAllArgs(), argTypes), keys = [...new Set(args.map(({ name }) => name))], frameKeys = keys.filter(key => frame.has(key)), horizAlignKeys = keys.filter(key => horizAlign.has(key)), vertAlignKeys = keys.filter(key => vertAlign.has(key)), [fr] = frameKeys, unscaled = fr === 'framed' || fr === 'manualthumb', rect = new rect_1.BoundingRect(this, start), { extension } = this;
85
85
  let rule = 'nested-link', s = index_1.default.lintConfig.getSeverity(rule, 'file');
86
86
  if (s
87
+ && extensions.has(extension)
87
88
  && this.closest('ext-link-text')
88
89
  && this.getValue('link')?.trim() !== '') {
89
- errors.push((0, lint_1.generateForSelf)(this, rect, rule, 'internal link in an external link', s));
90
+ const e = (0, lint_1.generateForSelf)(this, rect, rule, 'link-in-extlink', s), link = this.getArg('link');
91
+ if (link) {
92
+ const from = start + link.getRelativeIndex();
93
+ e.fix = { desc: index_1.default.msg('delink'), range: [from, from + link.toString().length], text: 'link=' };
94
+ }
95
+ else {
96
+ e.fix = (0, lint_1.fixByInsert)(e.endIndex - 2, 'delink', '|link=');
97
+ }
98
+ errors.push(e);
90
99
  }
91
100
  rule = 'invalid-gallery';
92
101
  s = index_1.default.lintConfig.getSeverity(rule, 'parameter');
93
102
  if (s && unscaled) {
94
103
  for (const arg of args.filter(({ name }) => name === 'width')) {
95
- const e = (0, lint_1.generateForChild)(arg, rect, rule, 'invalid image parameter', s);
96
- e.fix = { desc: 'remove', range: [e.startIndex - 1, e.endIndex], text: '' };
104
+ const e = (0, lint_1.generateForChild)(arg, rect, rule, 'invalid-image-parameter', s);
105
+ e.fix = (0, lint_1.fixByRemove)(e, -1);
97
106
  errors.push(e);
98
107
  }
99
108
  }
@@ -104,7 +113,7 @@ class FileToken extends base_1.LinkBaseToken {
104
113
  return errors;
105
114
  }
106
115
  rule = 'no-duplicate';
107
- const { extension } = this, severities = ['unknownImageParameter', 'imageParameter'].map(k => index_1.default.lintConfig.getSeverity(rule, k));
116
+ const severities = ['unknownImageParameter', 'imageParameter'].map(k => index_1.default.lintConfig.getSeverity(rule, k));
108
117
  /**
109
118
  * 图片参数到语法错误的映射
110
119
  * @param tokens 图片参数节点
@@ -117,8 +126,9 @@ class FileToken extends base_1.LinkBaseToken {
117
126
  if (!s) {
118
127
  return false;
119
128
  }
120
- const e = (0, lint_1.generateForChild)(arg, rect, rule, index_1.default.msg(`${msg} image $1 parameter`, p1), s);
121
- e.suggestions = [{ desc: 'remove', range: [e.startIndex - 1, e.endIndex], text: '' }];
129
+ /** `conflicting-image-parameter`或`duplicate-image-parameter` */
130
+ const e = (0, lint_1.generateForChild)(arg, rect, rule, index_1.default.msg(`${msg}-image-parameter`, p1), s);
131
+ e.suggestions = [(0, lint_1.fixByRemove)(e, -1)];
122
132
  return e;
123
133
  }).filter((e) => e !== false);
124
134
  for (const key of keys) {
@@ -139,7 +149,7 @@ class FileToken extends base_1.LinkBaseToken {
139
149
  const plainArgs = filterArgs(relevantArgs, transclusion);
140
150
  severity = plainArgs.length > 1 && ((arg) => plainArgs.includes(arg));
141
151
  }
142
- errors.push(...generate(relevantArgs, 'duplicated', key, severity));
152
+ errors.push(...generate(relevantArgs, 'duplicate', key, severity));
143
153
  }
144
154
  }
145
155
  if (frameKeys.length > 1) {
@@ -103,8 +103,8 @@ let GalleryImageToken = (() => {
103
103
  lint(start = this.getAbsoluteIndex(), re) {
104
104
  const errors = super.lint(start, re), rule = 'invalid-gallery', s = index_1.default.lintConfig.getSeverity(rule, 'image');
105
105
  if (s && this.#lint()) {
106
- const e = (0, lint_1.generateForSelf)(this, { start }, rule, 'invalid gallery image', s);
107
- e.suggestions = [{ desc: 'prefix', range: [start, start], text: 'File:' }];
106
+ const e = (0, lint_1.generateForSelf)(this, { start }, rule, 'invalid-gallery', s);
107
+ e.suggestions = [(0, lint_1.fixByInsert)(start, 'prefix', 'File:')];
108
108
  errors.push(e);
109
109
  }
110
110
  return errors;
@@ -12,4 +12,6 @@ export declare abstract class LinkToken extends LinkBaseToken {
12
12
  readonly childNodes: readonly [AtomToken] | readonly [AtomToken, Token];
13
13
  abstract get link(): Title;
14
14
  get type(): 'link';
15
+ /** link text / 链接显示文字 */
16
+ get innerText(): string;
15
17
  }
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.LinkToken = void 0;
7
+ const common_1 = require("@bhsd/common");
7
8
  const lint_1 = require("../../util/lint");
8
9
  const index_1 = __importDefault(require("../../index"));
9
10
  const base_1 = require("./base");
@@ -17,11 +18,19 @@ class LinkToken extends base_1.LinkBaseToken {
17
18
  get type() {
18
19
  return 'link';
19
20
  }
21
+ /** link text / 链接显示文字 */
22
+ get innerText() {
23
+ return this.length > 1
24
+ ? this.lastChild.text()
25
+ : (0, common_1.rawurldecode)(this.firstChild.text().replace(/^\s*:?/u, ''));
26
+ }
20
27
  /** @private */
21
28
  lint(start = this.getAbsoluteIndex(), re) {
22
29
  const errors = super.lint(start, re), rule = 'nested-link', s = index_1.default.lintConfig.getSeverity(rule);
23
30
  if (s && this.closest('ext-link-text')) {
24
- errors.push((0, lint_1.generateForSelf)(this, { start }, rule, 'internal link in an external link', s));
31
+ const e = (0, lint_1.generateForSelf)(this, { start }, rule, 'link-in-extlink', s);
32
+ e.fix = (0, lint_1.fixBy)(e, 'delink', this.innerText);
33
+ errors.push(e);
25
34
  }
26
35
  return errors;
27
36
  }