wikilint 2.16.3 → 2.16.4

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.
@@ -21,6 +21,10 @@ class LinkBaseToken extends index_2.Token {
21
21
  #bracket = true;
22
22
  #delimiter;
23
23
  #title;
24
+ /** 完整链接 */
25
+ get link() {
26
+ return this.#title;
27
+ }
24
28
  /**
25
29
  * @param link 链接标题
26
30
  * @param linkText 链接显示文字
@@ -91,9 +95,9 @@ class LinkBaseToken extends index_2.Token {
91
95
  errors.push(e);
92
96
  }
93
97
  if (type === 'link' || type === 'category') {
94
- const textNode = linkText?.childNodes.find((c) => c.type === 'text' && c.data.includes('|'));
98
+ const j = linkText?.childNodes.findIndex(c => c.type === 'text' && c.data.includes('|')), textNode = linkText?.childNodes[j];
95
99
  if (textNode) {
96
- const e = (0, lint_1.generateForChild)(linkText, rect, 'pipe-like', 'additional "|" in the link text', 'warning'), i = e.startIndex + textNode.getRelativeIndex();
100
+ const e = (0, lint_1.generateForChild)(linkText, rect, 'pipe-like', 'additional "|" in the link text', 'warning'), i = e.startIndex + linkText.getRelativeIndex(j);
97
101
  e.suggestions = [
98
102
  {
99
103
  desc: 'escape',
@@ -105,10 +109,10 @@ class LinkBaseToken extends index_2.Token {
105
109
  }
106
110
  }
107
111
  if (fragment !== undefined && !isLink(type)) {
108
- const e = (0, lint_1.generateForChild)(target, rect, 'no-ignored', 'useless fragment'), textNode = target.childNodes.find((c) => c.type === 'text' && c.data.includes('#'));
112
+ const e = (0, lint_1.generateForChild)(target, rect, 'no-ignored', 'useless fragment'), j = target.childNodes.findIndex(c => c.type === 'text' && c.data.includes('#')), textNode = target.childNodes[j];
109
113
  if (textNode) {
110
114
  e.fix = {
111
- range: [e.startIndex + textNode.getRelativeIndex() + textNode.data.indexOf('#'), e.endIndex],
115
+ range: [e.startIndex + target.getRelativeIndex(j) + textNode.data.indexOf('#'), e.endIndex],
112
116
  text: '',
113
117
  desc: 'remove',
114
118
  };
@@ -1,4 +1,5 @@
1
1
  import { LinkBaseToken } from './base';
2
+ import type { Title } from '../../lib/title';
2
3
  import type { Token, AtomToken } from '../../internal';
3
4
  /**
4
5
  * 分类
@@ -6,5 +7,6 @@ import type { Token, AtomToken } from '../../internal';
6
7
  */
7
8
  export declare abstract class CategoryToken extends LinkBaseToken {
8
9
  readonly childNodes: readonly [AtomToken] | readonly [AtomToken, Token];
10
+ abstract get link(): Title;
9
11
  get type(): 'category';
10
12
  }
@@ -1,5 +1,6 @@
1
1
  import { LinkBaseToken } from './base';
2
2
  import type { LintError } from '../../base';
3
+ import type { Title } from '../../lib/title';
3
4
  import type { Token, AtomToken } from '../../internal';
4
5
  /**
5
6
  * 内链
@@ -7,5 +8,6 @@ import type { Token, AtomToken } from '../../internal';
7
8
  */
8
9
  export declare abstract class LinkToken extends LinkBaseToken {
9
10
  readonly childNodes: readonly [AtomToken] | readonly [AtomToken, Token];
11
+ abstract get link(): Title;
10
12
  get type(): 'link';
11
13
  }
@@ -10,6 +10,7 @@ import type { Token, AtomToken } from '../../internal';
10
10
  export declare abstract class RedirectTargetToken extends LinkBaseToken {
11
11
  readonly childNodes: readonly [AtomToken] | readonly [AtomToken, NoincludeToken];
12
12
  abstract get lastChild(): AtomToken | NoincludeToken;
13
+ abstract get link(): Title;
13
14
  get type(): 'redirect-target';
14
15
  /**
15
16
  * @param link 链接标题
@@ -22,5 +22,10 @@ export declare abstract class MagicLinkToken extends Token {
22
22
  * @param type 类型
23
23
  */
24
24
  constructor(url?: string, type?: ExtLinkTypes, config?: Parser.Config, accum?: Token[]);
25
+ /**
26
+ * 获取网址
27
+ * @param articlePath 条目路径
28
+ */
29
+ getUrl(articlePath?: string): URL | string;
25
30
  }
26
31
  export {};
@@ -76,5 +76,27 @@ class MagicLinkToken extends index_2.Token {
76
76
  }
77
77
  return errors;
78
78
  }
79
+ /**
80
+ * 获取网址
81
+ * @param articlePath 条目路径
82
+ */
83
+ getUrl(articlePath) {
84
+ LSP: { // eslint-disable-line no-unused-labels
85
+ const { type } = this;
86
+ let { link } = this;
87
+ if (type === 'magic-link') {
88
+ if (link.startsWith('ISBN')) {
89
+ return this.normalizeTitle(`Special:BookSources/${link.slice(5)}`).getUrl(articlePath);
90
+ }
91
+ link = link.startsWith('RFC')
92
+ ? `https://tools.ietf.org/html/rfc${link.slice(4)}`
93
+ : `https://pubmed.ncbi.nlm.nih.gov/${link.slice(5)}`;
94
+ }
95
+ else if (link.startsWith('//')) {
96
+ link = `https:${link}`;
97
+ }
98
+ return new URL(link);
99
+ }
100
+ }
79
101
  }
80
102
  exports.MagicLinkToken = MagicLinkToken;
@@ -3,6 +3,9 @@ import { Token } from '../index';
3
3
  import { TagPairToken } from './index';
4
4
  import { AttributesToken } from '../attributes';
5
5
  import type { LintError } from '../../base';
6
+ import type { AttributesParentBase } from '../../mixin/attributesParent';
7
+ export interface ExtToken extends AttributesParentBase {
8
+ }
6
9
  /**
7
10
  * 扩展标签
8
11
  * @classdesc `{childNodes: [AttributesToken, Token]}`
@@ -1,9 +1,44 @@
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
  Object.defineProperty(exports, "__esModule", { value: true });
3
37
  exports.ExtToken = void 0;
4
38
  const lint_1 = require("../../util/lint");
5
39
  const rect_1 = require("../../lib/rect");
6
40
  const index_1 = require("../../index");
41
+ const attributesParent_1 = require("../../mixin/attributesParent");
7
42
  const index_2 = require("../index");
8
43
  const index_3 = require("./index");
9
44
  const attributes_1 = require("../attributes");
@@ -27,108 +62,124 @@ const del = (arr, ele) => {
27
62
  * 扩展标签
28
63
  * @classdesc `{childNodes: [AttributesToken, Token]}`
29
64
  */
30
- class ExtToken extends index_3.TagPairToken {
31
- get type() {
32
- return 'ext';
33
- }
34
- /**
35
- * @param name 标签名
36
- * @param include 是否嵌入
37
- * @param attr 标签属性
38
- * @param inner 内部wikitext
39
- * @param closed 是否封闭
40
- */
41
- constructor(name, attr, inner, closed, config = index_1.default.getConfig(), include = false, accum = []) {
42
- const lcName = name.toLowerCase(),
43
- // @ts-expect-error abstract class
44
- attrToken = new attributes_1.AttributesToken(!attr || /^\s/u.test(attr) ? attr : ` ${attr}`, 'ext-attrs', lcName, config, accum), newConfig = { ...config, ext: del(config.ext, lcName), excludes: [...config.excludes ?? []] };
45
- let innerToken;
46
- switch (lcName) {
47
- case 'tab':
48
- newConfig.ext = del(newConfig.ext, 'tabs');
49
- // fall through
50
- case 'indicator':
51
- case 'poem':
52
- case 'ref':
53
- case 'option':
54
- case 'combooption':
55
- case 'tabs':
56
- case 'poll':
57
- case 'seo':
58
- if (lcName === 'poem') {
65
+ let ExtToken = (() => {
66
+ let _classDecorators = [(0, attributesParent_1.attributesParent)()];
67
+ let _classDescriptor;
68
+ let _classExtraInitializers = [];
69
+ let _classThis;
70
+ let _classSuper = index_3.TagPairToken;
71
+ var ExtToken = class extends _classSuper {
72
+ static { _classThis = this; }
73
+ static {
74
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
75
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
76
+ ExtToken = _classThis = _classDescriptor.value;
77
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
78
+ __runInitializers(_classThis, _classExtraInitializers);
79
+ }
80
+ get type() {
81
+ return 'ext';
82
+ }
83
+ /**
84
+ * @param name 标签名
85
+ * @param include 是否嵌入
86
+ * @param attr 标签属性
87
+ * @param inner 内部wikitext
88
+ * @param closed 是否封闭
89
+ */
90
+ constructor(name, attr, inner, closed, config = index_1.default.getConfig(), include = false, accum = []) {
91
+ const lcName = name.toLowerCase(),
92
+ // @ts-expect-error abstract class
93
+ attrToken = new attributes_1.AttributesToken(!attr || /^\s/u.test(attr) ? attr : ` ${attr}`, 'ext-attrs', lcName, config, accum), newConfig = { ...config, ext: del(config.ext, lcName), excludes: [...config.excludes ?? []] };
94
+ let innerToken;
95
+ switch (lcName) {
96
+ case 'tab':
97
+ newConfig.ext = del(newConfig.ext, 'tabs');
98
+ // fall through
99
+ case 'indicator':
100
+ case 'poem':
101
+ case 'ref':
102
+ case 'option':
103
+ case 'combooption':
104
+ case 'tabs':
105
+ case 'poll':
106
+ case 'seo':
107
+ if (lcName === 'poem') {
108
+ newConfig.excludes.push('heading');
109
+ }
110
+ innerToken = new index_2.Token(inner, newConfig, accum);
111
+ break;
112
+ case 'pre':
113
+ // @ts-expect-error abstract class
114
+ innerToken = new pre_1.PreToken(inner, newConfig, accum);
115
+ break;
116
+ case 'dynamicpagelist':
117
+ // @ts-expect-error abstract class
118
+ innerToken = new index_4.ParamTagToken(include, inner, newConfig, accum);
119
+ break;
120
+ case 'inputbox':
121
+ newConfig.excludes.push('heading');
122
+ // @ts-expect-error abstract class
123
+ innerToken = new inputbox_1.InputboxToken(include, inner, newConfig, accum);
124
+ break;
125
+ case 'references': {
126
+ const { NestedToken } = require('../nested');
59
127
  newConfig.excludes.push('heading');
128
+ // @ts-expect-error abstract class
129
+ innerToken = new NestedToken(inner, include, ['ref'], newConfig, accum);
130
+ break;
60
131
  }
61
- innerToken = new index_2.Token(inner, newConfig, accum);
62
- break;
63
- case 'pre':
64
- // @ts-expect-error abstract class
65
- innerToken = new pre_1.PreToken(inner, newConfig, accum);
66
- break;
67
- case 'dynamicpagelist':
68
- // @ts-expect-error abstract class
69
- innerToken = new index_4.ParamTagToken(include, inner, newConfig, accum);
70
- break;
71
- case 'inputbox':
72
- newConfig.excludes.push('heading');
73
- // @ts-expect-error abstract class
74
- innerToken = new inputbox_1.InputboxToken(include, inner, newConfig, accum);
75
- break;
76
- case 'references': {
77
- const { NestedToken } = require('../nested');
78
- newConfig.excludes.push('heading');
79
- // @ts-expect-error abstract class
80
- innerToken = new NestedToken(inner, include, ['ref'], newConfig, accum);
81
- break;
82
- }
83
- case 'choose': {
84
- const { NestedToken } = require('../nested');
85
- // @ts-expect-error abstract class
86
- innerToken = new NestedToken(inner, /<(option|choicetemplate)(\s[^>]*?)?(?:\/>|>([\s\S]*?)<\/(\1)>)/gu, ['option', 'choicetemplate'], newConfig, accum);
87
- break;
132
+ case 'choose': {
133
+ const { NestedToken } = require('../nested');
134
+ // @ts-expect-error abstract class
135
+ innerToken = new NestedToken(inner, /<(option|choicetemplate)(\s[^>]*?)?(?:\/>|>([\s\S]*?)<\/(\1)>)/gu, ['option', 'choicetemplate'], newConfig, accum);
136
+ break;
137
+ }
138
+ case 'combobox': {
139
+ const { NestedToken } = require('../nested');
140
+ // @ts-expect-error abstract class
141
+ innerToken = new NestedToken(inner, /<(combooption)(\s[^>]*?)?(?:\/>|>([\s\S]*?)<\/(combooption\s*)>)/giu, ['combooption'], newConfig, accum);
142
+ break;
143
+ }
144
+ case 'gallery':
145
+ // @ts-expect-error abstract class
146
+ innerToken = new gallery_1.GalleryToken(inner, newConfig, accum);
147
+ break;
148
+ case 'imagemap':
149
+ // @ts-expect-error abstract class
150
+ innerToken = new imagemap_1.ImagemapToken(inner, newConfig, accum);
151
+ break;
152
+ // 更多定制扩展的代码示例:
153
+ // ```
154
+ // case 'extensionName': {
155
+ // const {ExtensionToken}: typeof import('../extension') = require('../extension');
156
+ // innerToken = new ExtensionToken(inner, newConfig, accum);
157
+ // break;
158
+ // }
159
+ // ```
160
+ default:
161
+ // @ts-expect-error abstract class
162
+ innerToken = new index_5.NowikiToken(inner, newConfig, accum);
88
163
  }
89
- case 'combobox': {
90
- const { NestedToken } = require('../nested');
91
- // @ts-expect-error abstract class
92
- innerToken = new NestedToken(inner, /<(combooption)(\s[^>]*?)?(?:\/>|>([\s\S]*?)<\/(combooption\s*)>)/giu, ['combooption'], newConfig, accum);
93
- break;
164
+ innerToken.setAttribute('name', lcName);
165
+ if (innerToken.type === 'plain') {
166
+ innerToken.type = 'ext-inner';
94
167
  }
95
- case 'gallery':
96
- // @ts-expect-error abstract class
97
- innerToken = new gallery_1.GalleryToken(inner, newConfig, accum);
98
- break;
99
- case 'imagemap':
100
- // @ts-expect-error abstract class
101
- innerToken = new imagemap_1.ImagemapToken(inner, newConfig, accum);
102
- break;
103
- // 更多定制扩展的代码示例:
104
- // ```
105
- // case 'extensionName': {
106
- // const {ExtensionToken}: typeof import('../extension') = require('../extension');
107
- // innerToken = new ExtensionToken(inner, newConfig, accum);
108
- // break;
109
- // }
110
- // ```
111
- default:
112
- // @ts-expect-error abstract class
113
- innerToken = new index_5.NowikiToken(inner, newConfig, accum);
168
+ super(name, attrToken, innerToken, closed, config, accum);
169
+ this.seal('closed', true);
114
170
  }
115
- innerToken.setAttribute('name', lcName);
116
- if (innerToken.type === 'plain') {
117
- innerToken.type = 'ext-inner';
118
- }
119
- super(name, attrToken, innerToken, closed, config, accum);
120
- this.seal('closed', true);
121
- }
122
- /** @private */
123
- lint(start = this.getAbsoluteIndex(), re) {
124
- const errors = super.lint(start, re), rect = new rect_1.BoundingRect(this, start);
125
- if (this.name !== 'nowiki' && this.closest('html-attrs,table-attrs')) {
126
- errors.push((0, lint_1.generateForSelf)(this, rect, 'parsing-order', 'extension tag in HTML tag attributes'));
127
- }
128
- if (this.name === 'ref' && this.closest('heading-title')) {
129
- errors.push((0, lint_1.generateForSelf)(this, rect, 'var-anchor', 'variable anchor in a section header'));
171
+ /** @private */
172
+ lint(start = this.getAbsoluteIndex(), re) {
173
+ const errors = super.lint(start, re), rect = new rect_1.BoundingRect(this, start);
174
+ if (this.name !== 'nowiki' && this.closest('html-attrs,table-attrs')) {
175
+ errors.push((0, lint_1.generateForSelf)(this, rect, 'parsing-order', 'extension tag in HTML tag attributes'));
176
+ }
177
+ if (this.name === 'ref' && this.closest('heading-title')) {
178
+ errors.push((0, lint_1.generateForSelf)(this, rect, 'var-anchor', 'variable anchor in a section header'));
179
+ }
180
+ return errors;
130
181
  }
131
- return errors;
132
- }
133
- }
182
+ };
183
+ return ExtToken = _classThis;
184
+ })();
134
185
  exports.ExtToken = ExtToken;
@@ -30,6 +30,11 @@ export declare abstract class TranscludeToken extends Token {
30
30
  setModifier(modifier: string): boolean;
31
31
  /** 是否是模板或模块 */
32
32
  isTemplate(): boolean;
33
+ /**
34
+ * 获取模块名和模块函数名
35
+ * @throws `Error` 仅用于模块
36
+ */
37
+ getModule(): [string, string | undefined];
33
38
  /**
34
39
  * @override
35
40
  * @param token 待插入的子节点
@@ -20,6 +20,7 @@ class TranscludeToken extends index_2.Token {
20
20
  #type = 'template';
21
21
  #raw = false;
22
22
  #args = new Map();
23
+ #title;
23
24
  get type() {
24
25
  return this.#type;
25
26
  }
@@ -131,6 +132,23 @@ class TranscludeToken extends index_2.Token {
131
132
  const isTemplate = this.type === 'template', child = this.childNodes[isTemplate ? 0 : 1];
132
133
  return this.normalizeTitle(child.toString(true), isTemplate ? 10 : 828);
133
134
  }
135
+ /**
136
+ * 获取模块名和模块函数名
137
+ * @throws `Error` 仅用于模块
138
+ */
139
+ getModule() {
140
+ LSP: { // eslint-disable-line no-unused-labels
141
+ /* istanbul ignore if */
142
+ if (this.type !== 'magic-word' || this.name !== 'invoke') {
143
+ throw new Error('TranscludeToken.getModule method is only for modules!');
144
+ }
145
+ return [
146
+ this.#getTitle().title,
147
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
148
+ this.childNodes[2]?.toString(true).trim(),
149
+ ];
150
+ }
151
+ }
134
152
  /** @private */
135
153
  afterBuild() {
136
154
  if (this.modifier.includes('\0')) {
@@ -140,7 +158,8 @@ class TranscludeToken extends index_2.Token {
140
158
  if (this.isTemplate()) {
141
159
  const isTemplate = this.type === 'template';
142
160
  if (isTemplate) {
143
- this.setAttribute('name', this.#getTitle().title);
161
+ this.#title = this.#getTitle();
162
+ this.setAttribute('name', this.#title.title);
144
163
  }
145
164
  }
146
165
  }
@@ -166,6 +185,8 @@ class TranscludeToken extends index_2.Token {
166
185
  switch (key) {
167
186
  case 'padding':
168
187
  return this.modifier.length + 2;
188
+ case 'title':
189
+ return this.#title;
169
190
  default:
170
191
  return super.getAttribute(key);
171
192
  }
@@ -185,11 +206,11 @@ class TranscludeToken extends index_2.Token {
185
206
  errors.push((0, lint_1.generateForChild)(childNodes[1], rect, 'invalid-invoke', 'illegal module name'));
186
207
  }
187
208
  else {
188
- const child = childNodes[invoke ? 1 : 0], textNode = child.childNodes.find((c) => c.type === 'text' && (0, string_1.decodeHtml)(c.data).includes('#'));
209
+ const child = childNodes[invoke ? 1 : 0], i = child.childNodes.findIndex(c => c.type === 'text' && (0, string_1.decodeHtml)(c.data).includes('#')), textNode = child.childNodes[i];
189
210
  if (textNode) {
190
211
  const e = (0, lint_1.generateForChild)(child, rect, 'no-ignored', 'useless fragment');
191
212
  e.fix = {
192
- range: [e.startIndex + textNode.getRelativeIndex() + textNode.data.indexOf('#'), e.endIndex],
213
+ range: [e.startIndex + child.getRelativeIndex(i) + textNode.data.indexOf('#'), e.endIndex],
193
214
  text: '',
194
215
  desc: 'remove',
195
216
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikilint",
3
- "version": "2.16.3",
3
+ "version": "2.16.4",
4
4
  "description": "A Node.js linter for MediaWiki markup",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -23,6 +23,7 @@
23
23
  "/bin/cli.js",
24
24
  "/dist/",
25
25
  "!/dist/test/",
26
+ "!/dist/bin/coverage.js",
26
27
  "!/dist/bin/declaration.js"
27
28
  ],
28
29
  "bin": {
@@ -1,20 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const fs = require("fs");
4
- // eslint-disable-next-line n/no-missing-require
5
- const { total: { statements: { pct } } } = require('../../coverage/coverage-summary.json');
6
- const colors = ['#4c1', '#dfb317', '#e05d44'];
7
- const re = new RegExp(colors.join('|'), 'u');
8
- let color;
9
- if (pct >= 80) {
10
- [color] = colors;
11
- }
12
- else if (pct >= 60) {
13
- [, color] = colors;
14
- }
15
- else {
16
- [, , color] = colors;
17
- }
18
- const svg = fs.readFileSync('coverage/badge.svg', 'utf8').replace(/\b\d{2}(?=%)/gu, String(Math.round(pct)))
19
- .replace(re, color);
20
- fs.writeFileSync('coverage/badge.svg', svg);