wikilint 2.46.0 → 2.46.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.
package/README.md CHANGED
@@ -7,6 +7,7 @@
7
7
  [![CI](https://github.com/bhsd-harry/wikiparser-node/actions/workflows/node.js.yml/badge.svg)](https://github.com/bhsd-harry/wikiparser-node/actions/workflows/node.js.yml)
8
8
  [![NPM downloads](https://img.shields.io/npm/dm/wikilint)](https://www.npmjs.com/package/wikilint)
9
9
  ![Coverage](./coverage/badge.svg)
10
+ [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/13661/badge)](https://www.bestpractices.dev/projects/13661)
10
11
 
11
12
  This is a minimal version of [WikiParser-Node](https://www.npmjs.com/package/wikiparser-node). The [Wikitext LSP](https://www.npmjs.com/package/wikitext-lsp) and the [WikiParser Language Server](https://marketplace.visualstudio.com/items?itemName=Bhsd.vscode-extension-wikiparser) VS Code extension is written based on this package.
12
13
 
@@ -41,7 +41,7 @@ const filterGadget = (id) => {
41
41
  const n = Number(id);
42
42
  return n < 2300 || n > 2303; // Gadget, Gadget talk, Gadget definition, Gadget definition talk
43
43
  };
44
- const pkg = 'wikilint', version = '2.46.0';
44
+ const pkg = 'wikilint', version = '2.46.2';
45
45
  /**
46
46
  * Get the parser configuration for a Wikimedia Foundation project.
47
47
  * @param site site nickname
package/dist/index.js CHANGED
@@ -135,7 +135,7 @@ const Parser = {
135
135
  /** @implements */
136
136
  msg(msg, arg = '') {
137
137
  LINT: return msg
138
- && (this.i18n[msg] ?? msg).replace('$1', this.msg(arg));
138
+ && (this.i18n[msg] ?? msg).replace('$1', () => this.msg(arg));
139
139
  },
140
140
  /** @implements */
141
141
  normalizeTitle(title, defaultNs = 0, include, config = Parser.getConfig(), opt, accum = []) {
@@ -191,6 +191,7 @@ const Parser = {
191
191
  }
192
192
  maxStage ??= constants_1.MAX_STAGE;
193
193
  config ??= this.getConfig();
194
+ // eslint-disable-next-line unicorn/prefer-smaller-scope
194
195
  let types;
195
196
  LSP: if (typeof maxStage !== 'number') {
196
197
  types = Array.isArray(maxStage) ? maxStage : [maxStage];
package/dist/lib/lsp.d.ts CHANGED
@@ -77,7 +77,7 @@ export declare class LanguageService implements LanguageServiceBase {
77
77
  * 提供文档高亮
78
78
  * @param text source Wikitext / 源代码
79
79
  * @param position position / 位置
80
- * @since v1.46.0
80
+ * @since v1.46.1
81
81
  */
82
82
  provideDocumentHighlights(text: string, position: Position): Promise<DocumentHighlight[] | undefined>;
83
83
  /**
package/dist/lib/lsp.js CHANGED
@@ -324,6 +324,7 @@ function getStylelintPos(rect, bottom, lineOrCode, columnOrOffset) {
324
324
  return (0, lint_1.getEndPos)(top, left, ...adjustPos(height, width, lineOrCode - start, columnOrOffset));
325
325
  }
326
326
  const lines = lineOrCode.slice(0, columnOrOffset).split(/\r?\n/u);
327
+ // eslint-disable-next-line unicorn/no-useless-recursion
327
328
  return getStylelintPos(rect, bottom, lines.length, lines.at(-1).length);
328
329
  }
329
330
  /**
@@ -576,7 +577,7 @@ class LanguageService {
576
577
  + '|'
577
578
  // attribute key ($8, $10)
578
579
  + String.raw `<(\w+)(?:\s(?:[^<>{}|=\s]+(?:\s*=\s*(?:[^\s"']\S*|(["']).*?\9))?(?=\s))*)?\s(\w*)`
579
- + ')$', 'iu'), allSwitches = doubleUnderscore.slice(0, 2).flat();
580
+ + ')$', 'iu'), allSwitches = doubleUnderscore.slice(0, 2).flat().map(s => s.toUpperCase());
580
581
  this.#completionConfig = [
581
582
  {
582
583
  re,
@@ -639,11 +640,12 @@ class LanguageService {
639
640
  return getCompletion(root.querySelectorAll('arg').filter(token => token.name && token !== cur)
640
641
  .map(({ name }) => name), 'Variable', match, position);
641
642
  }
642
- const [insensitive, sensitive] = this.config.parserFunction, next = curLine.charAt(character), colon = match.startsWith(':'), str = colon ? match.slice(1).trimStart() : match;
643
+ const [insensitive, sensitive] = this.config.parserFunction, colon = match.startsWith(':'), str = colon ? match.slice(1).trimStart() : match;
643
644
  if (mt[2] === '[[') { // link
644
645
  return getCompletion(root.querySelectorAll('link,file,category,redirect-target').filter(token => token !== cur).map(({ name }) => name), 'Folder', str, position);
645
646
  }
646
647
  // parser function or template
648
+ const next = curLine.charAt(character);
647
649
  let words = functions;
648
650
  if (next === ':') {
649
651
  words = functions.filter(s => !s.endsWith(':'));
@@ -687,20 +689,20 @@ class LanguageService {
687
689
  }
688
690
  ({ type, parentNode } = cur);
689
691
  }
690
- if (mt?.[7] !== undefined || type === 'image-parameter') { // image parameter
692
+ if (type === 'image-parameter' || mt?.[7] !== undefined) { // image parameter
691
693
  const index = root.indexFromPos(line, character), match = mt?.[7]?.trimStart()
692
694
  ?? this.#text.slice(cur.getAbsoluteIndex(), index).trimStart(), equal = this.#text[index] === '=';
693
695
  return getCompletion(params, 'Property', match, position)
694
696
  .filter(({ label }) => !equal || !/[= ]$/u.test(label));
695
697
  }
696
- else if (mt?.[8] !== undefined || type === 'attr-key') { // attribute key
697
- const tag = mt?.[8]?.toLowerCase() ?? parentNode.tag, key = mt?.[10]
698
- ?? cur.toString().slice(0, character - root.posFromIndex(cur.getAbsoluteIndex()).left);
698
+ else if (type === 'attr-key' || mt?.[8] !== undefined) { // attribute key
699
+ const tag = mt?.[8]?.toLowerCase() ?? parentNode.tag;
699
700
  if (!tags.has(tag)) {
700
701
  return undefined;
701
702
  }
702
- const thisHtmlAttrs = sharable_1.htmlAttrs[tag], thisExtAttrs = sharable_1.extAttrs[tag], extCompletion = thisExtAttrs && getCompletion(thisExtAttrs, 'Field', key, position);
703
- return ext.includes(tag) && !thisHtmlAttrs
703
+ const key = mt?.[10]
704
+ ?? cur.toString().slice(0, character - root.posFromIndex(cur.getAbsoluteIndex()).left), thisHtmlAttrs = sharable_1.htmlAttrs[tag], thisExtAttrs = sharable_1.extAttrs[tag], extCompletion = thisExtAttrs && getCompletion(thisExtAttrs, 'Field', key, position);
705
+ return !thisHtmlAttrs && ext.includes(tag)
704
706
  ? extCompletion
705
707
  : [
706
708
  ...extCompletion ?? [],
@@ -871,18 +873,16 @@ class LanguageService {
871
873
  source: 'Stylelint',
872
874
  code: rule,
873
875
  message: msg.slice(0, msg.lastIndexOf('(') - 1),
874
- ...fix
875
- ? {
876
- data: [
877
- {
878
- range: getStylelintPos(rects[i], bottoms[i], code, fix.range),
879
- newText: fix.text,
880
- title: `Fix: ${rule}`,
881
- fix: true,
882
- },
883
- ],
884
- }
885
- : {},
876
+ ...fix && {
877
+ data: [
878
+ {
879
+ range: getStylelintPos(rects[i], bottoms[i], code, fix.range),
880
+ newText: fix.text,
881
+ title: `Fix: ${rule}`,
882
+ fix: true,
883
+ },
884
+ ],
885
+ },
886
886
  };
887
887
  });
888
888
  }
@@ -1150,7 +1150,7 @@ class LanguageService {
1150
1150
  * 提供文档高亮
1151
1151
  * @param text source Wikitext / 源代码
1152
1152
  * @param position position / 位置
1153
- * @since v1.46.0
1153
+ * @since v1.46.1
1154
1154
  */
1155
1155
  async provideDocumentHighlights(text, position) {
1156
1156
  const root = await this.#queue(text), index = root.indexFromPos(position.line, position.character), node = root.elementFromIndex(index), type = node?.type;
@@ -1321,7 +1321,7 @@ class LanguageService {
1321
1321
  f = offsetNode.text().trim();
1322
1322
  colon = parentNode.getAttribute('colon');
1323
1323
  }
1324
- else if (offsetNode.is('magic-word') && !offsetNode.modifier && length === 1
1324
+ else if (length === 1 && offsetNode.is('magic-word') && !offsetNode.modifier
1325
1325
  && (offset > 0 || root.posFromIndex(offsetNode.getAbsoluteIndex()).left === position.character)) {
1326
1326
  info = this.#getParserFunction(name);
1327
1327
  f = offsetNode.firstChild.text().trim();
@@ -1432,9 +1432,9 @@ class LanguageService {
1432
1432
  label: `{{${f}${params.length === 0 ? '' : colon}${params.map(({ label }) => label).join('|')}}}`,
1433
1433
  parameters: params.map(({ label, const: c }) => ({
1434
1434
  label,
1435
- ...c ? { documentation: 'Predefined parameter' } : undefined,
1435
+ ...c && { documentation: 'Predefined parameter' },
1436
1436
  })),
1437
- ...params.length < n ? { activeParameter: Math.min(j, params.length - 1) } : undefined,
1437
+ ...params.length < n && { activeParameter: Math.min(j, params.length - 1) },
1438
1438
  })),
1439
1439
  activeParameter: j,
1440
1440
  };
package/dist/lib/text.js CHANGED
@@ -100,7 +100,8 @@ class AstText extends node_1.AstNode {
100
100
  }
101
101
  }
102
102
  errorRegex ??= parentNode.isPlain() && !noLinkTypes.has(type) ? errorSyntax : errorSyntaxUrl;
103
- if (data.search(errorRegex) === -1) {
103
+ errorRegex.lastIndex = 0;
104
+ if (!errorRegex.test(data)) {
104
105
  return [];
105
106
  }
106
107
  errorRegex.lastIndex = 0;
@@ -127,7 +128,7 @@ class AstText extends node_1.AstNode {
127
128
  }
128
129
  else if (error.startsWith('<') && !tags.has(tag.toLowerCase())
129
130
  || lbrackInExtLinkText && (/&(?:rbrack|#93|#x5[Dd];);/u.test(data.slice(index + 1))
130
- || nextSibling?.is('ext') && nextName === 'nowiki'
131
+ || nextName === 'nowiki' && nextSibling?.is('ext')
131
132
  && nextSibling.innerText?.includes(']'))) {
132
133
  continue;
133
134
  }
@@ -142,7 +143,7 @@ class AstText extends node_1.AstNode {
142
143
  errorRegex.lastIndex--;
143
144
  }
144
145
  // Rule & Severity
145
- let startIndex = start + index, endIndex = startIndex + length, rule, severity, endLine, endCol;
146
+ let startIndex = start + index, endIndex = startIndex + length, rule, severity;
146
147
  const nextChar = rootStr[endIndex], previousChar = rootStr[startIndex - 1], leftBracket = lbrace || lbrack, lConverter = error === '{' && previousChar === '-' && variants.length > 0, rConverter = error === '}' && nextChar === '-' && variants.length > 0, brokenExtLink = lbrack && nextType === 'free-ext-link' && !data.slice(index + 1).trim()
147
148
  || rbrack && previousType === 'free-ext-link' && index === 0;
148
149
  if (magicLink) {
@@ -222,6 +223,7 @@ class AstText extends node_1.AstNode {
222
223
  }
223
224
  // LintError
224
225
  const pos = this.posFromIndex(index), { line: startLine, character: startCol } = (0, lint_1.getEndPos)(top, left, pos.top + 1, pos.left);
226
+ let endLine, endCol;
225
227
  if (char === '=') {
226
228
  const lineEnd = data.indexOf('\n', index);
227
229
  let sibling = nextSibling, line;
package/dist/lib/title.js CHANGED
@@ -188,10 +188,9 @@ class Title {
188
188
  }
189
189
  const { title, fragment } = this;
190
190
  if (title) {
191
- return this.#path.replace('$1', encodeURIComponent(title)
192
- + (fragment
193
- ? `#${encodeURIComponent(fragment)}`
194
- : ''));
191
+ return this.#path.replace('$1', () => encodeURIComponent(title) + (fragment
192
+ ? `#${encodeURIComponent(fragment)}`
193
+ : ''));
195
194
  }
196
195
  return fragment === undefined ? '' : `#${encodeURIComponent(fragment)}`;
197
196
  }
@@ -31,11 +31,10 @@ const parseMagicLinks = (wikitext, config, accum) => {
31
31
  }
32
32
  const sep = url.includes('(') ? /[^,;\\.:!?][,;\\.:!?]+$/u : /[^,;\\.:!?)][,;\\.:!?)]+$/u, sepChars = sep.exec(url);
33
33
  if (sepChars) {
34
- let correction = 1;
35
- if (sepChars[0][1] === ';'
36
- && /&(?:[a-z]+|#x[\da-f]+|#\d+)$/iu.test(url.slice(0, sepChars.index))) {
37
- correction = 2;
38
- }
34
+ const correction = sepChars[0][1] === ';'
35
+ && /&(?:[a-z]+|#x[\da-f]+|#\d+)$/iu.test(url.slice(0, sepChars.index))
36
+ ? 2
37
+ : 1;
39
38
  trail = url.slice(sepChars.index + correction) + trail;
40
39
  url = url.slice(0, sepChars.index + correction);
41
40
  }
package/dist/src/arg.js CHANGED
@@ -76,20 +76,19 @@ let ArgToken = (() => {
76
76
  super(undefined, config, accum, {});
77
77
  for (let i = 0; i < parts.length; i++) {
78
78
  const part = parts[i];
79
+ let token;
79
80
  if (i === 0) {
80
- const token = new atom_1.AtomToken(part, 'arg-name', config, accum, {});
81
- super.insertAt(token);
81
+ token = new atom_1.AtomToken(part, 'arg-name', config, accum, {});
82
82
  }
83
83
  else if (i > 1) {
84
- const token = new hidden_1.HiddenToken(part, config, accum);
85
- super.insertAt(token);
84
+ token = new hidden_1.HiddenToken(part, config, accum);
86
85
  }
87
86
  else {
88
- const token = new index_2.Token(part, config, accum);
87
+ token = new index_2.Token(part, config, accum);
89
88
  token.type = 'arg-default';
90
89
  token.setAttribute('stage', 2);
91
- super.insertAt(token);
92
90
  }
91
+ super.insertAt(token);
93
92
  }
94
93
  }
95
94
  /** @private */
@@ -122,7 +121,7 @@ let ArgToken = (() => {
122
121
  let rule = 'no-arg', s = lintConfig.getSeverity(rule);
123
122
  if (s && !this.getAttribute('include')) {
124
123
  const e = (0, lint_1.generateForSelf)(this, rect, rule, 'unexpected-argument', s), [, argDefault] = this.childNodes;
125
- if (lintConfig.computeEditInfo && argDefault) {
124
+ if (argDefault && lintConfig.computeEditInfo) {
126
125
  e.suggestions = [(0, lint_1.fixBy)(e, 'expand', argDefault.text())];
127
126
  }
128
127
  errors.push(e);
@@ -66,7 +66,7 @@ class AttributeToken extends index_2.Token {
66
66
  }
67
67
  else if (tag === 'gallery' && key === 'caption'
68
68
  || tag === 'ref' && key === 'details'
69
- || (tag === 'mapframe' || tag === 'maplink') && key === 'text'
69
+ || key === 'text' && (tag === 'mapframe' || tag === 'maplink')
70
70
  || tag === 'choose' && (key === 'before' || key === 'after')) {
71
71
  const newConfig = {
72
72
  ...config,
@@ -203,7 +203,7 @@ class AttributeToken extends index_2.Token {
203
203
  lint(start = this.getAbsoluteIndex(), re) {
204
204
  LINT: {
205
205
  const errors = super.lint(start, re), { balanced, firstChild, lastChild, name, tag } = this, rect = new rect_1.BoundingRect(this, start), rules = ['unclosed-quote', 'obsolete-attr'], { lintConfig } = index_1.default, s = rules.map(rule => lintConfig.getSeverity(rule, name));
206
- if (s[0] && !balanced) {
206
+ if (!balanced && s[0]) {
207
207
  const e = (0, lint_1.generateForChild)(lastChild, rect, rules[0], 'unclosed-quotes', s[0]);
208
208
  e.startIndex--;
209
209
  e.startCol--;
@@ -221,8 +221,8 @@ class AttributeToken extends index_2.Token {
221
221
  }
222
222
  /* NOT FOR BROWSER ONLY */
223
223
  const rule = 'invalid-css', sError = lintConfig.getSeverity(rule), sWarn = lintConfig.getSeverity(rule, 'warn');
224
- if ((sError || sWarn)
225
- && name === 'style'
224
+ if (name === 'style'
225
+ && (sError || sWarn)
226
226
  && lastChild.length === 1 && lastChild.firstChild.type === 'text') {
227
227
  const cssLSP = (0, document_1.loadCssLSP)();
228
228
  if (cssLSP) {
@@ -126,7 +126,7 @@ let HeadingToken = (() => {
126
126
  }
127
127
  errors.push(e);
128
128
  }
129
- if (severities[1] && unbalanced) {
129
+ if (unbalanced && severities[1]) {
130
130
  const msg = index_1.default.msg('unbalanced-in-section-header', '"="'), e = (0, lint_1.generateForChild)(firstChild, rect, rules[1], msg, severities[1]);
131
131
  if (!computeEditInfo || innerStr === '=') {
132
132
  //
@@ -65,24 +65,19 @@ const getUrl = (link) => {
65
65
  const pdfExtensions = new Set(['djvu', 'djv', 'pdf']);
66
66
  function validate(key, val, config, extOrType, halfParsed, accum) {
67
67
  val = (0, string_1.removeComment)(val).trim();
68
- let value = val.replace(key === 'link' ? /\0\d+[tq]\x7F/gu : /\0\d+t\x7F/gu, '').trim();
68
+ const value = val.replace(key === 'link' ? /\0\d+[tq]\x7F/gu : /\0\d+t\x7F/gu, '').trim();
69
69
  switch (key) {
70
70
  case 'width':
71
71
  return !value && Boolean(val) || /^(?:\d+x?|\d*x\d+)(?:\s*px)?$/u.test(value);
72
72
  case 'link': {
73
73
  const isGalleryImage = extOrType === 'gallery-image';
74
- let nowiki = true;
75
74
  if (!value) {
76
75
  return val;
77
76
  }
78
77
  else if (getUrlLikeRegex(config.protocol).test(value)) {
79
78
  return getUrlRegex(config.protocol).test(value) ? val : isGalleryImage;
80
79
  }
81
- else if (value.startsWith('[[') && value.endsWith(']]')) {
82
- value = value.slice(2, -2);
83
- nowiki = false;
84
- }
85
- const title = index_1.default.normalizeTitle(value, 0, false, config, { halfParsed, decode: true, selfLink: true, page: '', nowiki }, accum);
80
+ const title = index_1.default.normalizeTitle(value, 0, false, config, { halfParsed, decode: true, selfLink: true, page: '', nowiki: true }, accum);
86
81
  return title.valid ? title : isGalleryImage;
87
82
  }
88
83
  case 'lang':
@@ -135,18 +130,20 @@ let ImageParameterToken = (() => {
135
130
  /** image link / 图片链接 */
136
131
  get link() {
137
132
  LINT: {
138
- if (this.name !== 'link') {
133
+ const { name, parentNode, childNodes } = this;
134
+ if (name !== 'link') {
139
135
  return undefined;
140
136
  }
141
- const value = super.text().trim();
137
+ const value = (0, string_1.text)(childNodes.flatMap(child => child.is('link') ? child.lastChild.childNodes : child)
138
+ .filter(({ type }) => type !== 'html')).trim();
142
139
  return debug_1.Shadow.run(() => {
143
140
  const config = this.getAttribute('config'), accum = [], token = new index_2.Token(value, config, accum);
144
141
  token.parseOnce(0, this.getAttribute('include')).parseOnce();
145
142
  if (/^\0\d+m\x7F/u.test(token.firstChild.toString())) {
146
143
  return value;
147
144
  }
148
- const link = validate('link', value, config, this.parentNode?.type, undefined, accum);
149
- return link === true ? undefined : link;
145
+ const link = validate('link', value, config, parentNode?.type, undefined, accum);
146
+ return typeof link === 'boolean' ? undefined : link;
150
147
  }, index_1.default);
151
148
  }
152
149
  }
@@ -198,7 +195,8 @@ let ImageParameterToken = (() => {
198
195
  }
199
196
  /** @private */
200
197
  toString(skip) {
201
- return this.#syntax ? this.#syntax.replace('$1', super.toString(skip)) : super.toString(skip);
198
+ const str = super.toString(skip);
199
+ return this.#syntax ? this.#syntax.replace('$1', () => str) : str;
202
200
  }
203
201
  /** @private */
204
202
  text() {
@@ -210,7 +208,7 @@ let ImageParameterToken = (() => {
210
208
  if (name !== 'caption') {
211
209
  str = str.trim();
212
210
  }
213
- return this.#syntax ? this.#syntax.replace('$1', str).trim() : str;
211
+ return this.#syntax ? this.#syntax.replace('$1', () => str).trim() : str;
214
212
  }
215
213
  /** @private */
216
214
  isPlain() {
package/dist/src/index.js CHANGED
@@ -445,7 +445,7 @@ class Token extends element_1.AstElement {
445
445
  break;
446
446
  }
447
447
  else if (rules && !rules.has(rule)) {
448
- continue;
448
+ //
449
449
  }
450
450
  else if (line === startLine) {
451
451
  return false;
@@ -63,7 +63,6 @@ class MagicLinkToken extends index_2.Token {
63
63
  if (this.type === 'magic-link') {
64
64
  const { link } = this;
65
65
  if (link.startsWith('ISBN')) {
66
- // eslint-disable-next-line unicorn/no-useless-spread
67
66
  const digits = [...link.slice(5)].map(s => s === 'X' ? 10 : Number(s));
68
67
  return digits.length === 10
69
68
  ? digits.reduce((sum, d, i) => sum + d * (10 - i), 0) % 11 !== 0
@@ -34,8 +34,8 @@ class QuoteToken extends base_1.NowikiBaseToken {
34
34
  */
35
35
  get closing() {
36
36
  LINT: return {
37
- ...this.bold ? { bold: this.#closing.bold } : undefined,
38
- ...this.italic ? { italic: this.#closing.italic } : undefined,
37
+ ...this.bold && { bold: this.#closing.bold },
38
+ ...this.italic && { italic: this.#closing.italic },
39
39
  };
40
40
  }
41
41
  /** @param closing 是否闭合 */
@@ -160,7 +160,7 @@ let TdToken = (() => {
160
160
  if (computeEditInfo || fix) {
161
161
  if (double) {
162
162
  const syntax = { caption: '|+', td: '|', th: '!' }[this.subtype];
163
- e.fix = (0, lint_1.fixBy)(e, 'newline', data.replace(/\|\|/gu, `\n${syntax}`));
163
+ e.fix = (0, lint_1.fixBy)(e, 'newline', data.replace(/\|\|/gu, () => `\n${syntax}`));
164
164
  }
165
165
  else if (computeEditInfo) {
166
166
  e.suggestions = [(0, lint_1.fixByPipe)(e.startIndex, data)];
@@ -91,7 +91,7 @@ let IncludeToken = (() => {
91
91
  }
92
92
  errors.push(e);
93
93
  }
94
- if (s[1] && !closed) {
94
+ if (!closed && s[1]) {
95
95
  const e = (0, lint_1.generateForSelf)(this, rect, rules[1], index_1.default.msg('unclosed', `<${name}>`), s[1]);
96
96
  if (computeEditInfo) {
97
97
  e.suggestions = [(0, lint_1.fixByClose)(e.endIndex, `</${name}>`)];
@@ -113,7 +113,7 @@ let TranscludeToken = (() => {
113
113
  const m = /^(?:\s|\0\d+[cn]\x7F)*\0(\d+)h\x7F(?:\s|\0\d+[cn]\x7F)*/u.exec(title);
114
114
  if (m) {
115
115
  heading = Number(m[1]);
116
- title = title.replace(`\0${heading}h\x7F`, accum[heading].toString().replace(/^\n/u, ''));
116
+ title = title.replace(`\0${heading}h\x7F`, () => accum[heading].toString().replace(/^\n/u, ''));
117
117
  }
118
118
  super(undefined, config, accum, {});
119
119
  const { parserFunction, variable, functionHook } = config, argSubst = /^(?:\s|\0\d+[cn]\x7F)*\0\d+s\x7F/u.exec(title)?.[0];
@@ -274,41 +274,43 @@ let TranscludeToken = (() => {
274
274
  /** @private */
275
275
  lint(start = this.getAbsoluteIndex(), re) {
276
276
  LINT: {
277
- const errors = super.lint(start, re);
278
- if (!this.isTemplate()) {
279
- return errors;
280
- }
281
- const { type, childNodes, length: l } = this, rect = new rect_1.BoundingRect(this, start), { lintConfig } = index_1.default, { computeEditInfo } = lintConfig, invoke = type === 'magic-word';
282
- let rule = 'no-ignored', s = lintConfig.getSeverity(rule, 'fragment');
283
- if (invoke && !this.#getTitle().valid) {
284
- rule = 'invalid-invoke';
285
- s = lintConfig.getSeverity(rule, 'name');
286
- if (s) {
287
- errors.push((0, lint_1.generateForChild)(childNodes[1], rect, rule, 'illegal-module', s));
277
+ const errors = super.lint(start, re), { type, childNodes, length: l, name } = this, rect = new rect_1.BoundingRect(this, start), { lintConfig } = index_1.default, { computeEditInfo } = lintConfig, isTemplate = this.isTemplate();
278
+ let rule, s;
279
+ if (isTemplate) {
280
+ const invoke = type === 'magic-word';
281
+ if (invoke && !this.#getTitle().valid) {
282
+ rule = 'invalid-invoke';
283
+ s = lintConfig.getSeverity(rule, 'name');
284
+ if (s) {
285
+ errors.push((0, lint_1.generateForChild)(childNodes[1], rect, rule, 'illegal-module', s));
286
+ }
288
287
  }
289
- }
290
- else if (s) {
291
- const child = childNodes[invoke ? 1 : 0], i = child.childNodes
292
- .findIndex(c => c.type === 'text' && (0, string_1.decodeHtml)(c.data).includes('#')), textNode = child.childNodes[i];
293
- if (textNode) {
294
- const e = (0, lint_1.generateForChild)(child, rect, rule, 'useless-fragment', s);
295
- if (computeEditInfo) {
296
- e.suggestions = [
297
- (0, lint_1.fixByRemove)(e, child.getRelativeIndex(i) + textNode.data.indexOf('#')),
298
- ];
288
+ else {
289
+ rule = 'no-ignored';
290
+ s = lintConfig.getSeverity(rule, 'fragment');
291
+ if (s) {
292
+ const child = childNodes[invoke ? 1 : 0], i = child.childNodes
293
+ .findIndex(c => c.type === 'text' && (0, string_1.decodeHtml)(c.data).includes('#')), textNode = child.childNodes[i];
294
+ if (textNode) {
295
+ const e = (0, lint_1.generateForChild)(child, rect, rule, 'useless-fragment', s);
296
+ if (computeEditInfo) {
297
+ e.suggestions = [
298
+ (0, lint_1.fixByRemove)(e, child.getRelativeIndex(i) + textNode.data.indexOf('#')),
299
+ ];
300
+ }
301
+ errors.push(e);
302
+ }
299
303
  }
300
- errors.push(e);
301
304
  }
302
- }
303
- rule = 'invalid-invoke';
304
- s = lintConfig.getSeverity(rule, 'function');
305
- if (s && invoke && l === 2) {
306
- errors.push((0, lint_1.generateForSelf)(this, rect, rule, 'missing-function', s));
307
- return errors;
305
+ rule = 'invalid-invoke';
306
+ s = lintConfig.getSeverity(rule, 'function');
307
+ if (s && invoke && l === 2) {
308
+ errors.push((0, lint_1.generateForSelf)(this, rect, rule, 'missing-function', s));
309
+ }
308
310
  }
309
311
  rule = 'no-duplicate';
310
312
  s = lintConfig.getSeverity(rule, 'parameter');
311
- if (s) {
313
+ if (s && (isTemplate || name === 'tag')) {
312
314
  const duplicatedArgs = this.getDuplicatedArgs()
313
315
  .filter(([, parameter]) => !parameter[0].querySelector('ext'));
314
316
  for (const [, args] of duplicatedArgs) {
@@ -378,7 +380,14 @@ let TranscludeToken = (() => {
378
380
  */
379
381
  getDuplicatedArgs() {
380
382
  LINT: {
381
- return [...this.#args].filter(([, { size }]) => size > 1).map(([key, args]) => [key, [...args]]);
383
+ const isTemplate = this.isTemplate();
384
+ const duplicatedArgs = [...this.#args].filter(([, { size }]) => size > 1)
385
+ .map(([key, args]) => [key, [...args]]);
386
+ return isTemplate
387
+ ? duplicatedArgs
388
+ : duplicatedArgs
389
+ .map(([key, args]) => [key, args.filter(({ anon }) => !anon)])
390
+ .filter(([, args]) => args.length > 1);
382
391
  }
383
392
  }
384
393
  /**
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.extParams = exports.obsoleteAttrs = exports.extAttrs = exports.htmlAttrs = exports.commonHtmlAttrs = void 0;
4
- /* eslint-disable unicorn/no-unreadable-iife */
5
4
  const blockAttrs = new Set(['align']), citeAttrs = new Set(['cite']), citeAndAttrs = new Set(['cite', 'datetime']), widthAttrs = new Set(['width']), trAttrs = new Set(['bgcolor', 'align', 'valign']), obsoleteTdAttrs = new Set([...trAttrs, 'axis', 'height', 'nowrap', 'width']), tdAttrs = new Set([...obsoleteTdAttrs, 'abbr', 'headers', 'scope', 'rowspan', 'colspan']), typeAttrs = new Set(['type']), obsoleteTableAttrs = new Set(['summary', 'align', 'bgcolor', 'cellpadding', 'cellspacing', 'frame', 'rules', 'width']), brAttrs = new Set(['clear']), chemAttrs = new Set(['id', 'qid', 'forcemathmode', 'type', 'display']), syntaxHighlightAttrs = new Set([
6
5
  'enclose',
7
6
  'inline',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikilint",
3
- "version": "2.46.0",
3
+ "version": "2.46.2",
4
4
  "description": "A Node.js linter for MediaWiki markup",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -98,24 +98,24 @@
98
98
  }
99
99
  },
100
100
  "dependencies": {
101
- "@bhsd/cm-util": "^2.3.0",
102
- "@bhsd/common": "^4.0.0",
101
+ "@bhsd/cm-util": "^2.4.0",
102
+ "@bhsd/common": "^4.0.1",
103
103
  "@bhsd/stylelint-util": "^1.1.0",
104
+ "culori": "^4.0.2",
104
105
  "entities": "^8.0.0",
105
106
  "vscode-languageserver-types": "^3.18.0"
106
107
  },
107
108
  "devDependencies": {
108
- "@bhsd/code-standard": "^3.1.0",
109
+ "@bhsd/code-standard": "^4.0.0",
109
110
  "@bhsd/nodejs": "^1.2.1",
110
111
  "@bhsd/test-util": "^3.1.0",
111
- "c8": "^11.0.0",
112
- "culori": "^4.0.2",
112
+ "c8": "^12.0.0",
113
113
  "diff2html-cli": "^5.2.15",
114
114
  "esbuild": "^0.28.1",
115
- "eslint": "^10.7.0",
116
- "markdownlint-cli2": "^0.23.0",
115
+ "eslint": "^10.8.0",
116
+ "markdownlint-cli2": "^0.23.1",
117
117
  "mathoid-texvcjs": "^0.6.0",
118
- "stylelint": "^17.14.0",
118
+ "stylelint": "^17.14.1",
119
119
  "typescript": "^6.0.3",
120
120
  "v8r": "^6.1.0",
121
121
  "vscode-css-languageservice": "^6.3.10",