wikilint 2.32.0 → 2.34.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 (62) hide show
  1. package/dist/base.d.mts +1 -1
  2. package/dist/base.d.ts +1 -1
  3. package/dist/base.js +1 -0
  4. package/dist/base.mjs +1 -0
  5. package/dist/bin/cli.js +8 -19
  6. package/dist/bin/config.js +1 -1
  7. package/dist/index.js +3 -5
  8. package/dist/internal.d.ts +3 -0
  9. package/dist/lib/lintConfig.js +8 -0
  10. package/dist/lib/lsp.js +20 -3
  11. package/dist/lib/node.js +3 -11
  12. package/dist/lib/text.js +18 -18
  13. package/dist/src/attribute.d.ts +3 -5
  14. package/dist/src/attribute.js +11 -9
  15. package/dist/src/attributes.d.ts +0 -1
  16. package/dist/src/attributes.js +4 -5
  17. package/dist/src/imageParameter.d.ts +7 -3
  18. package/dist/src/imageParameter.js +14 -4
  19. package/dist/src/index.d.ts +8 -0
  20. package/dist/src/index.js +22 -20
  21. package/dist/src/link/base.d.ts +1 -1
  22. package/dist/src/link/base.js +7 -1
  23. package/dist/src/link/categorytree.d.ts +19 -0
  24. package/dist/src/link/categorytree.js +95 -0
  25. package/dist/src/link/file.d.ts +3 -2
  26. package/dist/src/link/file.js +3 -2
  27. package/dist/src/link/galleryImage.js +5 -4
  28. package/dist/src/multiLine/gallery.d.ts +1 -3
  29. package/dist/src/multiLine/gallery.js +1 -3
  30. package/dist/src/multiLine/imagemap.js +2 -3
  31. package/dist/src/multiLine/inputbox.d.ts +3 -3
  32. package/dist/src/multiLine/inputbox.js +3 -7
  33. package/dist/src/multiLine/paramTag.d.ts +3 -4
  34. package/dist/src/multiLine/paramTag.js +5 -47
  35. package/dist/src/nowiki/index.js +48 -13
  36. package/dist/src/nowiki/list.d.ts +0 -1
  37. package/dist/src/nowiki/list.js +1 -0
  38. package/dist/src/nowiki/noinclude.d.ts +0 -1
  39. package/dist/src/nowiki/noinclude.js +1 -0
  40. package/dist/src/paramLine.d.ts +3 -0
  41. package/dist/src/paramLine.js +43 -2
  42. package/dist/src/table/base.d.ts +1 -2
  43. package/dist/src/table/base.js +7 -8
  44. package/dist/src/table/index.d.ts +1 -1
  45. package/dist/src/table/index.js +1 -7
  46. package/dist/src/table/td.d.ts +0 -1
  47. package/dist/src/table/td.js +1 -0
  48. package/dist/src/table/tr.d.ts +1 -1
  49. package/dist/src/tagPair/ext.js +19 -10
  50. package/dist/src/tagPair/index.d.ts +2 -1
  51. package/dist/src/tagPair/index.js +10 -6
  52. package/dist/src/tagPair/translate.d.ts +1 -1
  53. package/dist/src/tagPair/translate.js +3 -0
  54. package/dist/util/debug.js +10 -3
  55. package/dist/util/diff.js +6 -17
  56. package/dist/util/sharable.d.mts +1 -0
  57. package/dist/util/sharable.js +62 -1
  58. package/dist/util/sharable.mjs +62 -0
  59. package/i18n/en.json +2 -0
  60. package/i18n/zh-hans.json +2 -0
  61. package/i18n/zh-hant.json +2 -0
  62. package/package.json +3 -3
package/dist/base.d.mts CHANGED
@@ -44,7 +44,7 @@ export declare const stages: {
44
44
  converter: number;
45
45
  };
46
46
  export type Stage = keyof typeof stages;
47
- export declare const rules: readonly ["arg-in-ext", "bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "invalid-url", "lonely-apos", "lonely-bracket", "lonely-http", "nested-link", "no-arg", "no-duplicate", "no-ignored", "obsolete-attr", "obsolete-tag", "parsing-order", "pipe-like", "syntax-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", "invalid-css", "invalid-math"];
47
+ export declare const rules: readonly ["arg-in-ext", "bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "invalid-json", "invalid-url", "lonely-apos", "lonely-bracket", "lonely-http", "nested-link", "no-arg", "no-duplicate", "no-ignored", "obsolete-attr", "obsolete-tag", "parsing-order", "pipe-like", "syntax-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", "invalid-css", "invalid-math"];
48
48
  export declare namespace LintError {
49
49
  type Severity = 'error' | 'warning';
50
50
  type Rule = typeof rules[number];
package/dist/base.d.ts CHANGED
@@ -44,7 +44,7 @@ export declare const stages: {
44
44
  converter: number;
45
45
  };
46
46
  export type Stage = keyof typeof stages;
47
- export declare const rules: readonly ["arg-in-ext", "bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "invalid-url", "lonely-apos", "lonely-bracket", "lonely-http", "nested-link", "no-arg", "no-duplicate", "no-ignored", "obsolete-attr", "obsolete-tag", "parsing-order", "pipe-like", "syntax-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", "invalid-css", "invalid-math"];
47
+ export declare const rules: readonly ["arg-in-ext", "bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "invalid-json", "invalid-url", "lonely-apos", "lonely-bracket", "lonely-http", "nested-link", "no-arg", "no-duplicate", "no-ignored", "obsolete-attr", "obsolete-tag", "parsing-order", "pipe-like", "syntax-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", "invalid-css", "invalid-math"];
48
48
  export declare namespace LintError {
49
49
  type Severity = 'error' | 'warning';
50
50
  type Rule = typeof rules[number];
package/dist/base.js CHANGED
@@ -44,6 +44,7 @@ exports.rules = (() => {
44
44
  'invalid-imagemap',
45
45
  'invalid-invoke',
46
46
  'invalid-isbn',
47
+ 'invalid-json',
47
48
  'invalid-url',
48
49
  'lonely-apos',
49
50
  'lonely-bracket',
package/dist/base.mjs CHANGED
@@ -41,6 +41,7 @@ const rules = /* @__PURE__ */ (() => {
41
41
  "invalid-imagemap",
42
42
  "invalid-invoke",
43
43
  "invalid-isbn",
44
+ "invalid-json",
44
45
  "invalid-url",
45
46
  "lonely-apos",
46
47
  "lonely-bracket",
package/dist/bin/cli.js CHANGED
@@ -7,21 +7,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const assert_1 = __importDefault(require("assert"));
10
+ const util_1 = __importDefault(require("util"));
10
11
  const index_1 = __importDefault(require("../index"));
11
- const chalk = (() => {
12
- try {
13
- return require('chalk');
14
- }
15
- catch {
16
- const f = ((text) => text), proxy = new Proxy(f, {
17
- /** @private */
18
- get() {
19
- return proxy;
20
- },
21
- });
22
- return proxy;
23
- }
24
- })();
25
12
  const man = `
26
13
  Available options:
27
14
  -c, --config <path or preset config> Choose parser's configuration
@@ -159,11 +146,13 @@ const throwOnCacheFile = (input) => {
159
146
  * @param word item name
160
147
  */
161
148
  const plural = (n, word) => `${n} ${word}${n === 1 ? '' : 's'}`;
149
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
150
+ const styleText = util_1.default.styleText ?? ((_, text) => text);
162
151
  /**
163
152
  * color the severity
164
153
  * @param severity problem severity
165
154
  */
166
- const coloredSeverity = (severity) => chalk[severity === 'error' ? 'red' : 'yellow'](severity.padEnd(7));
155
+ const coloredSeverity = (severity) => styleText(severity === 'error' ? 'red' : 'yellow', severity.padEnd(7));
167
156
  for (let i = 2; i < argv.length; i++) {
168
157
  option = argv[i];
169
158
  switch (option) {
@@ -374,10 +363,10 @@ catch {
374
363
  nFixableWarn += nLocalFixableWarn;
375
364
  }
376
365
  if (problems.length > 0) {
377
- console.error(`\n${chalk.underline('%s')}`, file);
366
+ console.error('\n%s', styleText('underline', file));
378
367
  const maxLineChars = String(Math.max(...problems.map(({ startLine }) => startLine))).length, maxColChars = String(Math.max(...problems.map(({ startCol }) => startCol))).length, maxMessageChars = Math.max(...problems.map(({ message: { length } }) => length));
379
368
  for (const { rule, message, severity, startLine, startCol } of problems) {
380
- console.error(` ${chalk.dim('%s:%s')} %s %s ${chalk.dim('%s')}`, String(startLine).padStart(maxLineChars), String(startCol).padEnd(maxColChars), coloredSeverity(severity), message.padEnd(maxMessageChars), rule);
369
+ console.error(` ${styleText('dim', '%s:%s')} %s %s %s`, String(startLine).padStart(maxLineChars), String(startCol).padEnd(maxColChars), coloredSeverity(severity), message.padEnd(maxMessageChars), styleText('dim', rule));
381
370
  }
382
371
  }
383
372
  nErr += nLocalErr;
@@ -385,9 +374,9 @@ catch {
385
374
  exiting ||= Boolean(nLocalErr || strict && nLocalWarn);
386
375
  }
387
376
  if (nErr || nWarn) {
388
- console.error(chalk.red.bold('%s'), `\n✖ ${plural(nErr + nWarn, 'problem')} (${plural(nErr, 'error')}, ${plural(nWarn, 'warning')})`);
377
+ console.error(styleText(['red', 'bold'], '%s'), `\n✖ ${plural(nErr + nWarn, 'problem')} (${plural(nErr, 'error')}, ${plural(nWarn, 'warning')})`);
389
378
  if (nFixableErr || nFixableWarn) {
390
- console.error(chalk.red.bold('%s'), ` ${plural(nFixableErr, 'error')} and ${plural(nFixableWarn, 'warning')} potentially fixable with the \`--fix\` option.`);
379
+ console.error(styleText(['red', 'bold'], '%s'), ` ${plural(nFixableErr, 'error')} and ${plural(nFixableWarn, 'warning')} potentially fixable with the \`--fix\` option.`);
391
380
  }
392
381
  console.error();
393
382
  }
@@ -77,7 +77,7 @@ const mw = {
77
77
  },
78
78
  },
79
79
  };
80
- const pkg = "wikilint", version = "2.32.0";
80
+ const pkg = "wikilint", version = "2.34.0";
81
81
  let mwConfig;
82
82
  /**
83
83
  * Get the parser configuration for a Wikimedia Foundation project.
package/dist/index.js CHANGED
@@ -172,11 +172,9 @@ const Parser = {
172
172
  maxStage ??= constants_1.MAX_STAGE;
173
173
  config ??= this.getConfig();
174
174
  let types;
175
- LINT: {
176
- if (typeof maxStage !== 'number') {
177
- types = Array.isArray(maxStage) ? maxStage : [maxStage];
178
- maxStage = Math.max(...types.map(t => base_1.stages[t] || constants_1.MAX_STAGE));
179
- }
175
+ LINT: if (typeof maxStage !== 'number') {
176
+ types = Array.isArray(maxStage) ? maxStage : [maxStage];
177
+ maxStage = Math.max(...types.map(t => base_1.stages[t] || constants_1.MAX_STAGE));
180
178
  }
181
179
  const { Token } = require('./src/index');
182
180
  const root = debug_1.Shadow.run(() => {
@@ -45,5 +45,8 @@ export type { GalleryToken } from './src/multiLine/gallery';
45
45
  export type { ImagemapLinkToken } from './src/imagemapLink';
46
46
  export type { ImagemapToken } from './src/multiLine/imagemap';
47
47
  export type { CommentedToken } from './src/commented';
48
+ export type { CategorytreeToken } from './src/link/categorytree';
49
+ export type { ParamLineToken } from './src/paramLine';
50
+ export type { CommentLineToken } from './src/nowiki/commentLine';
48
51
  export type { TranslateToken } from './src/tagPair/translate';
49
52
  export type { TvarToken } from './src/tag/tvar';
@@ -52,6 +52,7 @@ const defaultLintRuleConfig = {
52
52
  {
53
53
  // extension: 2,
54
54
  // image: 2,
55
+ // link: 2,
55
56
  parameter: 1,
56
57
  // thumb: 2,
57
58
  },
@@ -71,6 +72,12 @@ const defaultLintRuleConfig = {
71
72
  },
72
73
  ],
73
74
  'invalid-isbn': 2,
75
+ 'invalid-json': [
76
+ 2,
77
+ {
78
+ duplicate: 1,
79
+ },
80
+ ],
74
81
  'invalid-url': 1,
75
82
  'lonely-apos': [
76
83
  1,
@@ -128,6 +135,7 @@ const defaultLintRuleConfig = {
128
135
  // invalidAttributes: 2,
129
136
  nonWordAttributes: 1,
130
137
  redirect: 1,
138
+ // categorytree: 2,
131
139
  // choose: 2,
132
140
  // combobox: 2,
133
141
  // dynamicpagelist: 2,
package/dist/lib/lsp.js CHANGED
@@ -741,6 +741,13 @@ class LanguageService {
741
741
  return token.parentNode.module === mod && token.parentNode.function === func;
742
742
  }).map(({ name }) => name), 'Variable', key, position, type === 'parameter-value' ? '=' : '')
743
743
  : undefined;
744
+ }
745
+ else if (type === 'param-line') {
746
+ // parameter line of `<dynamicpagelist>` or `<inputbox>`
747
+ const key = this.#text.slice(cur.getAbsoluteIndex(), root.indexFromPos(line, character)).trimStart();
748
+ return /^[a-z]+$/iu.test(key)
749
+ ? getCompletion(sharable_1.extParams[cur.name], 'Property', key, position)
750
+ : undefined;
744
751
  /* NOT FOR BROWSER ONLY */
745
752
  }
746
753
  else if (isAttr(cur, true)) {
@@ -813,8 +820,21 @@ class LanguageService {
813
820
  async provideDiagnostics(text, warning = true) {
814
821
  const root = await this.#queue(text), { lintConfig } = index_1.default, needFix = lintConfig.fix;
815
822
  lintConfig.fix = false;
823
+ /* NOT FOR BROWSER ONLY */
824
+ const stylelint = await (0, document_1.loadStylelint)(), jsonLSP = (0, document_1.loadJsonLSP)();
825
+ let s;
826
+ NPM: if (jsonLSP) {
827
+ s = lintConfig.rules['invalid-json'];
828
+ lintConfig.rules['invalid-json'] = 0;
829
+ }
830
+ /* NOT FOR BROWSER ONLY END */
816
831
  const errors = root.lint();
817
832
  lintConfig.fix = needFix;
833
+ /* NOT FOR BROWSER ONLY */
834
+ NPM: if (jsonLSP) {
835
+ lintConfig.rules['invalid-json'] = s;
836
+ }
837
+ /* NOT FOR BROWSER ONLY END */
818
838
  const diagnostics = (warning ? errors : errors.filter(({ severity }) => severity === 'error')).map(({ startLine, startCol, endLine, endCol, severity, rule, message, fix, suggestions,
819
839
  /* NOT FOR BROWSER ONLY */
820
840
  code, }) => ({
@@ -836,9 +856,6 @@ class LanguageService {
836
856
  ...suggestions ? suggestions.map(suggestion => getQuickFix(root, suggestion)) : [],
837
857
  ],
838
858
  })),
839
- /* NOT FOR BROWSER ONLY */
840
- stylelint = await (0, document_1.loadStylelint)(), jsonLSP = (0, document_1.loadJsonLSP)(),
841
- /* NOT FOR BROWSER ONLY END */
842
859
  /* eslint-disable @stylistic/operator-linebreak */
843
860
  cssDiagnostics = stylelint ?
844
861
  await (async () => {
package/dist/lib/node.js CHANGED
@@ -39,6 +39,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.AstNode = void 0;
41
41
  /* eslint-disable @typescript-eslint/no-base-to-string */
42
+ const common_1 = require("@bhsd/common");
42
43
  const search_1 = __importDefault(require("../util/search"));
43
44
  const lint_1 = require("../util/lint");
44
45
  const debug_1 = require("../util/debug");
@@ -141,7 +142,7 @@ let AstNode = (() => {
141
142
  * @param left column number / 列号
142
143
  */
143
144
  indexFromPos(top, left) {
144
- LSP: {
145
+ LINT: {
145
146
  if (top < 0 || left < 0) {
146
147
  return undefined;
147
148
  }
@@ -248,16 +249,7 @@ let AstNode = (() => {
248
249
  * @since v1.16.3
249
250
  */
250
251
  getLines() {
251
- LINT: {
252
- const results = [];
253
- let start = 0;
254
- for (const line of String(this).split('\n')) {
255
- const end = start + line.length;
256
- results.push([line, start, end]);
257
- start = end + 1;
258
- }
259
- return results;
260
- }
252
+ LINT: return (0, common_1.splitLines)(String(this));
261
253
  }
262
254
  };
263
255
  return AstNode = _classThis;
package/dist/lib/text.js CHANGED
@@ -88,10 +88,10 @@ class AstText extends node_1.AstNode {
88
88
  }
89
89
  const { type, parentNode: grandparent } = parentNode;
90
90
  if (type === 'attr-value') {
91
- const { name: grandName, tag } = grandparent;
92
- if (tag === 'ref' && (grandName === 'name' || grandName === 'follow')
93
- || grandName === 'group' && (tag === 'ref' || tag === 'references')
94
- || tag === 'choose' && (grandName === 'before' || grandName === 'after')) {
91
+ const { name, tag } = grandparent;
92
+ if (tag === 'ref' && (name === 'name' || name === 'follow')
93
+ || name === 'group' && (tag === 'ref' || tag === 'references')
94
+ || tag === 'choose' && (name === 'before' || name === 'after')) {
95
95
  return [];
96
96
  }
97
97
  }
@@ -114,9 +114,15 @@ class AstText extends node_1.AstNode {
114
114
  ...lintConfig.getSeverity('tag-like', 'disallowed') ? disallowedTags : [],
115
115
  ]);
116
116
  for (let mt = errorRegex.exec(data); mt; mt = errorRegex.exec(data)) {
117
- const [, tag, prefix] = mt;
118
117
  let { index, 0: error } = mt;
119
- if (prefix && prefix !== ']') {
118
+ const [, tag, prefix] = mt, lbrackInExtLinkText = error === '[' && type === 'ext-link-text';
119
+ if (error.startsWith('<') && !tags.has(tag.toLowerCase())
120
+ || lbrackInExtLinkText && (/&(?:rbrack|#93|#x5[Dd];);/u.test(data.slice(index + 1))
121
+ || nextSibling?.is('ext') && nextName === 'nowiki'
122
+ && nextSibling.innerText?.includes(']'))) {
123
+ continue;
124
+ }
125
+ else if (prefix && prefix !== ']') {
120
126
  const { length } = prefix;
121
127
  index += length;
122
128
  error = error.slice(length);
@@ -126,20 +132,14 @@ class AstText extends node_1.AstNode {
126
132
  error = error.slice(1);
127
133
  }
128
134
  error = error.toLowerCase();
129
- const [char] = error, magicLink = char === 'r' || char === 'p' || char === 'i', lbrace = char === '{', rbrace = char === '}', lbrack = char === '[', rbrack = char === ']';
135
+ const [char] = error, magicLink = error === 'rfc' || error === 'pmid' || error === 'isbn', lbrace = char === '{', rbrace = char === '}', lbrack = char === '[', rbrack = char === ']';
130
136
  let { length } = error;
131
- if (char === '<' && !tags.has(tag.toLowerCase())
132
- || lbrack && type === 'ext-link-text' && (/&(?:rbrack|#93|#x5[Dd];);/u.test(data.slice(index + 1))
133
- || nextSibling?.is('ext') && nextName === 'nowiki'
134
- && nextSibling.innerText?.includes(']'))) {
135
- continue;
136
- }
137
- else if (rbrack && (index || length > 1)) {
137
+ if (rbrack && (index || length > 1)) {
138
138
  errorRegex.lastIndex--;
139
139
  }
140
140
  // Rule & Severity
141
141
  let startIndex = start + index, endIndex = startIndex + length, rule, severity, endLine, endCol;
142
- const nextChar = rootStr[endIndex], previousChar = rootStr[startIndex - 1], leftBracket = lbrace || lbrack, lConverter = lbrace && previousChar === '-' && variants.length > 0, rConverter = rbrace && nextChar === '-' && variants.length > 0, brokenExtLink = lbrack && nextType === 'free-ext-link' && !data.slice(index + 1).trim()
142
+ 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()
143
143
  || rbrack && previousType === 'free-ext-link'
144
144
  && !data.slice(0, index).includes(']');
145
145
  if (magicLink) {
@@ -190,7 +190,7 @@ class AstText extends node_1.AstNode {
190
190
  severity = lintConfig.getSeverity(rule, 'double');
191
191
  }
192
192
  else {
193
- if (!lbrack || type !== 'ext-link-text') {
193
+ if (!lbrackInExtLinkText) {
194
194
  const regex = regexes[char], remains = leftBracket ? data.slice(index + 1) : data.slice(0, index);
195
195
  if (lbrace && regex.exec(remains)?.[0] === '}'
196
196
  || rbrace && regex.exec(remains)?.[0] === '{') {
@@ -272,11 +272,11 @@ class AstText extends node_1.AstNode {
272
272
  else if (char === 'h' && type !== 'link-text' && wordRegex.test(previousChar || '')) {
273
273
  e.suggestions = [(0, lint_1.fixBySpace)(startIndex)];
274
274
  }
275
- else if (lbrack && type === 'ext-link-text') {
275
+ else if (lbrackInExtLinkText) {
276
276
  const i = parentNode.getAbsoluteIndex() + parentNode.toString().length;
277
277
  e.suggestions = [(0, lint_1.fixByEscape)(i, '&#93;')];
278
278
  }
279
- else if (rbrack && brokenExtLink) {
279
+ else if (error === ']' && brokenExtLink) {
280
280
  const i = start - previousSibling.toString().length;
281
281
  e.suggestions = [(0, lint_1.fixByInsert)(i, 'left-bracket', '[')];
282
282
  }
@@ -1,7 +1,6 @@
1
- import Parser from '../index';
2
1
  import { Token } from './index';
3
2
  import { AtomToken } from './atom';
4
- import type { LintError } from '../base';
3
+ import type { LintError, Config } from '../base';
5
4
  import type { AttributesToken } from '../internal';
6
5
  export type AttributeTypes = 'ext-attr' | 'html-attr' | 'table-attr';
7
6
  /**
@@ -28,16 +27,15 @@ export declare abstract class AttributeToken extends Token {
28
27
  * @param type 标签类型
29
28
  * @param tag 标签名
30
29
  * @param key 属性名
30
+ * @param quotes 引号
31
31
  * @param equal 等号
32
32
  * @param value 属性值
33
- * @param quotes 引号
34
33
  */
35
- constructor(type: AttributeTypes, tag: string, key: string, equal?: string, value?: string, quotes?: readonly [string?, string?], config?: Parser.Config, accum?: Token[]);
34
+ constructor(type: AttributeTypes, tag: string, key: string, quotes: readonly [string?, string?], config: Config, equal?: string, value?: string, accum?: Token[]);
36
35
  /**
37
36
  * Get the attribute value
38
37
  *
39
38
  * 获取属性值
40
39
  */
41
40
  getValue(): string | true;
42
- escape(): void;
43
41
  }
@@ -52,11 +52,11 @@ class AttributeToken extends index_2.Token {
52
52
  * @param type 标签类型
53
53
  * @param tag 标签名
54
54
  * @param key 属性名
55
+ * @param quotes 引号
55
56
  * @param equal 等号
56
57
  * @param value 属性值
57
- * @param quotes 引号
58
58
  */
59
- constructor(type, tag, key, equal = '', value, quotes = [], config = index_1.default.getConfig(), accum = []) {
59
+ constructor(type, tag, key, quotes, config, equal = '', value, accum = []) {
60
60
  const keyToken = new atom_1.AtomToken(key, 'attr-key', config, accum);
61
61
  let valueToken;
62
62
  if (key === 'title' || tag === 'img' && key === 'alt') {
@@ -129,9 +129,12 @@ class AttributeToken extends index_2.Token {
129
129
  }
130
130
  if (!attrs?.has(name)
131
131
  && !attrs2?.has(name)
132
- // 不是未定义的扩展标签或包含嵌入的HTML标签
132
+ // 已知定义的扩展标签或不包含嵌入的HTML标签
133
133
  && (type === 'ext-attr' ? attrs || attrs2 : !/\{\{[^{]+\}\}/u.test(name))
134
- && (type === 'ext-attr' && !attrs2
134
+ && (
135
+ // 不支持通用HTML属性的扩展标签
136
+ type === 'ext-attr' && !attrs2
137
+ // 或非通用HTML属性
135
138
  || !/^(?:xmlns:[\w:.-]+|data-(?!ooui|mw|parsoid)[^:]*)$/u.test(name)
136
139
  && (tag === 'meta' || tag === 'link' || !sharable_1.commonHtmlAttrs.has(name)))
137
140
  || (name === 'itemtype' || name === 'itemid' || name === 'itemref')
@@ -247,12 +250,11 @@ class AttributeToken extends index_2.Token {
247
250
  getValue() {
248
251
  return this.#equal ? this.lastChild.text().trim() : this.type === 'ext-attr' || '';
249
252
  }
253
+ /** @private */
250
254
  escape() {
251
- LSP: {
252
- if (this.type !== 'ext-attr') {
253
- this.#equal = '{{=}}';
254
- this.lastChild.escape();
255
- }
255
+ LSP: if (this.type !== 'ext-attr') {
256
+ this.#equal = '{{=}}';
257
+ this.lastChild.escape();
256
258
  }
257
259
  }
258
260
  }
@@ -60,6 +60,5 @@ export declare abstract class AttributesToken extends Token {
60
60
  * @param key attribute name / 属性键
61
61
  */
62
62
  getAttr(key: string): string | true | undefined;
63
- escape(): void;
64
63
  }
65
64
  export {};
@@ -66,7 +66,7 @@ class AttributesToken extends index_2.Token {
66
66
  if (/^(?:[\w:]|\0\d+t\x7F)(?:[\w:.-]|\0\d+t\x7F)*$/u.test((0, string_1.removeComment)(key).trim())) {
67
67
  const value = quoted ?? unquoted, quotes = [quoteStart, quoteEnd],
68
68
  // @ts-expect-error abstract class
69
- token = new attribute_1.AttributeToken((0, exports.toAttributeType)(type), name, key, equal, value, quotes, config, accum);
69
+ token = new attribute_1.AttributeToken((0, exports.toAttributeType)(type), name, key, quotes, config, equal, value, accum);
70
70
  insertDirty();
71
71
  super.insertAt(token);
72
72
  }
@@ -195,11 +195,10 @@ class AttributesToken extends index_2.Token {
195
195
  return errors;
196
196
  }
197
197
  }
198
+ /** @private */
198
199
  escape() {
199
- LSP: {
200
- if (this.type !== 'ext-attrs') {
201
- super.escape();
202
- }
200
+ LSP: if (this.type !== 'ext-attrs') {
201
+ super.escape();
203
202
  }
204
203
  }
205
204
  }
@@ -1,5 +1,5 @@
1
1
  import { Token } from './index';
2
- import type { LintError, Config } from '../base';
2
+ import type { LintError, Config, TokenTypes } from '../base';
3
3
  import type { Title } from '../lib/title';
4
4
  import type { AtomToken, FileToken } from '../internal';
5
5
  /**
@@ -23,8 +23,12 @@ export declare abstract class ImageParameterToken extends Token {
23
23
  get thumb(): Title | undefined;
24
24
  /** image link / 图片链接 */
25
25
  get link(): string | Title | undefined;
26
- /** @param str 图片参数 */
27
- constructor(str: string, extension: string | undefined, config: Config, accum?: Token[]);
26
+ /**
27
+ * @param str 图片参数
28
+ * @param extension 文件扩展名
29
+ * @param type 父节点类型
30
+ */
31
+ constructor(str: string, extension: string | undefined, type: TokenTypes | undefined, config: Config, accum?: Token[]);
28
32
  /**
29
33
  * Get the parameter value
30
34
  *
@@ -147,15 +147,19 @@ let ImageParameterToken = (() => {
147
147
  }, index_1.default);
148
148
  }
149
149
  }
150
- /** @param str 图片参数 */
151
- constructor(str, extension, config, accum) {
150
+ /**
151
+ * @param str 图片参数
152
+ * @param extension 文件扩展名
153
+ * @param type 父节点类型
154
+ */
155
+ constructor(str, extension, type, config, accum) {
152
156
  let mt;
153
157
  const regexes = Object.entries(config.img)
154
158
  .map(([syntax, param]) => [syntax, param, getSyntaxRegex(syntax)]), param = regexes.find(([, key, regex]) => {
155
159
  mt = regex.exec(str);
156
160
  return mt
157
161
  && (mt.length !== 4
158
- || validate(key, mt[2], config, extension, true) !== false);
162
+ || validate(key, mt[2], config, key === 'link' ? type : extension, true) !== false);
159
163
  });
160
164
  // @ts-expect-error mt already assigned
161
165
  if (param && mt) {
@@ -222,7 +226,13 @@ let ImageParameterToken = (() => {
222
226
  }
223
227
  }
224
228
  else if (name === 'link') {
225
- if (typeof link === 'string') {
229
+ if (link === undefined) {
230
+ const rule = 'invalid-gallery', s = lintConfig.getSeverity(rule, 'link');
231
+ if (s) {
232
+ errors.push((0, lint_1.generateForSelf)(this, { start }, rule, 'invalid-gallery-link', s));
233
+ }
234
+ }
235
+ else if (typeof link === 'string') {
226
236
  const rule = 'invalid-url', s = lintConfig.getSeverity(rule);
227
237
  if (s && !this.querySelector('magic-word')) {
228
238
  try {
@@ -34,5 +34,13 @@ export declare class Token extends AstElement {
34
34
  */
35
35
  insertAt(child: string, i?: number): AstText;
36
36
  insertAt<T extends AstNodes>(child: T, i?: number): T;
37
+ /**
38
+ * Normalize page title
39
+ *
40
+ * 规范化页面标题
41
+ * @param title title (with or without the namespace prefix) / 标题(含或不含命名空间前缀)
42
+ * @param defaultNs default namespace number / 命名空间
43
+ */
44
+ normalizeTitle(title: string, defaultNs?: number, opt?: TitleOptions): Title;
37
45
  }
38
46
  export {};
package/dist/src/index.js CHANGED
@@ -112,7 +112,7 @@ class Token extends element_1.AstElement {
112
112
  if (n < this.#stage || this.length !== 1 || !this.isPlain()) {
113
113
  return this;
114
114
  }
115
- else if (this.#stage >= constants_1.MAX_STAGE) {
115
+ else /* istanbul ignore if */ if (this.#stage >= constants_1.MAX_STAGE) {
116
116
  return this;
117
117
  }
118
118
  switch (n) {
@@ -170,10 +170,8 @@ class Token extends element_1.AstElement {
170
170
  if (i % 2 === 0) {
171
171
  return s && new text_1.AstText(s);
172
172
  }
173
- else if (isNaN(s.slice(-1))) {
174
- return this.#accum[Number(s.slice(0, -1))];
175
- }
176
- throw new Error(`Failed to build! Unrecognized token: ${s}`);
173
+ const n = Number(s.slice(0, -1));
174
+ return this.#accum[n];
177
175
  }).filter(node => node !== '');
178
176
  if (type === constants_1.BuildMethod.String) {
179
177
  return nodes.map(String).join('');
@@ -221,7 +219,7 @@ class Token extends element_1.AstElement {
221
219
  #parseRedirect() {
222
220
  const { parseRedirect } = require('../parser/redirect');
223
221
  const wikitext = this.firstChild.toString(), parsed = parseRedirect(wikitext, this.#config, this.#accum);
224
- if (parsed) {
222
+ if (parsed !== false) {
225
223
  this.setText(parsed);
226
224
  }
227
225
  return Boolean(parsed);
@@ -277,9 +275,6 @@ class Token extends element_1.AstElement {
277
275
  * @param tidy 是否整理
278
276
  */
279
277
  #parseQuotes(tidy) {
280
- if (this.#config.excludes.includes('quote')) {
281
- return;
282
- }
283
278
  const { parseQuotes } = require('../parser/quotes');
284
279
  const lines = this.firstChild.toString().split('\n');
285
280
  for (let i = 0; i < lines.length; i++) {
@@ -289,17 +284,11 @@ class Token extends element_1.AstElement {
289
284
  }
290
285
  /** 解析外部链接 */
291
286
  #parseExternalLinks() {
292
- if (this.#config.excludes.includes('extLink')) {
293
- return;
294
- }
295
287
  const { parseExternalLinks } = require('../parser/externalLinks');
296
288
  this.setText(parseExternalLinks(this.firstChild.toString(), this.#config, this.#accum));
297
289
  }
298
290
  /** 解析自由外链 */
299
291
  #parseMagicLinks() {
300
- if (this.#config.excludes.includes('magicLink')) {
301
- return;
302
- }
303
292
  const { parseMagicLinks } = require('../parser/magicLinks');
304
293
  this.setText(parseMagicLinks(this.firstChild.toString(), this.#config, this.#accum));
305
294
  }
@@ -338,6 +327,7 @@ class Token extends element_1.AstElement {
338
327
  return this.#accum;
339
328
  case 'built':
340
329
  return this.#built;
330
+ /* istanbul ignore next */
341
331
  case 'stage':
342
332
  return this.#stage;
343
333
  default:
@@ -358,7 +348,8 @@ class Token extends element_1.AstElement {
358
348
  }
359
349
  }
360
350
  insertAt(child, i = this.length) {
361
- const token = typeof child === 'string' ? new text_1.AstText(child) : child;
351
+ const token = typeof child === 'string' ? new text_1.AstText(child) : child, { length } = this;
352
+ i += i < 0 ? length : 0;
362
353
  super.insertAt(token, i);
363
354
  const { type, } = token;
364
355
  if (type === 'root') {
@@ -366,7 +357,14 @@ class Token extends element_1.AstElement {
366
357
  }
367
358
  return token;
368
359
  }
369
- /** @private */
360
+ // eslint-disable-next-line jsdoc/require-param
361
+ /**
362
+ * Normalize page title
363
+ *
364
+ * 规范化页面标题
365
+ * @param title title (with or without the namespace prefix) / 标题(含或不含命名空间前缀)
366
+ * @param defaultNs default namespace number / 命名空间
367
+ */
370
368
  normalizeTitle(title, defaultNs = 0, opt) {
371
369
  return index_1.default.normalizeTitle(title, defaultNs, this.getAttribute('include'), this.#config, { page: this.pageName, ...opt });
372
370
  }
@@ -427,8 +425,12 @@ class Token extends element_1.AstElement {
427
425
  let mt = regex.exec(wikitext);
428
426
  while (mt) {
429
427
  const { 1: type, index } = mt, detail = mt[2]?.trim();
428
+ let line;
429
+ if (type === 'disable-line' || type === 'disable-next-line') {
430
+ line = this.posFromIndex(index).top + (type === 'disable-line' ? 0 : 1);
431
+ }
430
432
  ignores.push({
431
- line: this.posFromIndex(index).top + (type === 'disable-line' ? 0 : 1),
433
+ line,
432
434
  from: type === 'disable' ? regex.lastIndex : undefined,
433
435
  to: type === 'enable' ? regex.lastIndex : undefined,
434
436
  rules: detail ? new Set(detail.split(',').map(rule => rule.trim())) : undefined,
@@ -438,13 +440,13 @@ class Token extends element_1.AstElement {
438
440
  errors = errors.filter(({ rule, startLine, startIndex }) => {
439
441
  const nearest = { pos: 0 };
440
442
  for (const { line, from, to, rules } of ignores) {
441
- if (line > startLine + 1) {
443
+ if (line && line > startLine + 1) {
442
444
  break;
443
445
  }
444
446
  else if (rules && !rules.has(rule)) {
445
447
  continue;
446
448
  }
447
- else if (line === startLine && from === undefined && to === undefined) {
449
+ else if (line === startLine) {
448
450
  return false;
449
451
  }
450
452
  else if (from <= startIndex && from > nearest.pos) {