wikilint 2.13.0 → 2.13.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.
@@ -140,10 +140,6 @@
140
140
  "711": "TimedText talk",
141
141
  "828": "Module",
142
142
  "829": "Module talk",
143
- "2300": "Gadget",
144
- "2301": "Gadget talk",
145
- "2302": "Gadget definition",
146
- "2303": "Gadget definition talk",
147
143
  "2600": "Topic"
148
144
  },
149
145
  "nsid": {
@@ -300,11 +296,7 @@
300
296
  "模組討論": 829,
301
297
  "模组对话": 829,
302
298
  "模组讨论": 829,
303
- "モジュール・トーク": 829,
304
- "gadget": 2300,
305
- "gadget talk": 2301,
306
- "gadget definition": 2302,
307
- "gadget definition talk": 2303
299
+ "モジュール・トーク": 829
308
300
  },
309
301
  "parserFunction": [
310
302
  {
@@ -125,11 +125,7 @@
125
125
  "710": "TimedText",
126
126
  "711": "TimedText talk",
127
127
  "828": "Module",
128
- "829": "Module talk",
129
- "2300": "Gadget",
130
- "2301": "Gadget talk",
131
- "2302": "Gadget definition",
132
- "2303": "Gadget definition talk"
128
+ "829": "Module talk"
133
129
  },
134
130
  "nsid": {
135
131
  "": 0,
@@ -165,11 +161,7 @@
165
161
  "timedtext": 710,
166
162
  "timedtext talk": 711,
167
163
  "module": 828,
168
- "module talk": 829,
169
- "gadget": 2300,
170
- "gadget talk": 2301,
171
- "gadget definition": 2302,
172
- "gadget definition talk": 2303
164
+ "module talk": 829
173
165
  },
174
166
  "parserFunction": [
175
167
  {
package/dist/base.d.ts CHANGED
@@ -9,7 +9,6 @@ export interface Config {
9
9
  readonly img: Record<string, string>;
10
10
  readonly redirection: string[];
11
11
  readonly variants: string[];
12
- readonly excludes?: string[];
13
12
  }
14
13
  export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | 'onlyinclude' | 'noinclude' | 'include' | 'comment' | 'ext' | 'ext-attrs' | 'ext-attr-dirty' | 'ext-attr' | 'attr-key' | 'attr-value' | 'ext-inner' | 'arg' | 'arg-name' | 'arg-default' | 'hidden' | 'magic-word' | 'magic-word-name' | 'invoke-function' | 'invoke-module' | 'template' | 'template-name' | 'parameter' | 'parameter-key' | 'parameter-value' | 'heading' | 'heading-title' | 'heading-trail' | 'html' | 'html-attrs' | 'html-attr-dirty' | 'html-attr' | 'table' | 'tr' | 'td' | 'table-syntax' | 'table-attrs' | 'table-attr-dirty' | 'table-attr' | 'table-inter' | 'td-inner' | 'hr' | 'double-underscore' | 'link' | 'link-target' | 'link-text' | 'category' | 'file' | 'gallery-image' | 'imagemap-image' | 'image-parameter' | 'quote' | 'ext-link' | 'ext-link-text' | 'ext-link-url' | 'free-ext-link' | 'magic-link' | 'list' | 'dd' | 'list-range' | 'converter' | 'converter-flags' | 'converter-flag' | 'converter-rule' | 'converter-rule-variant' | 'converter-rule-to' | 'converter-rule-from' | 'param-line' | 'imagemap-link';
15
14
  export declare const stages: {
package/dist/index.js CHANGED
@@ -92,6 +92,11 @@ const Parser = {
92
92
  catch (e) {
93
93
  if (e instanceof Error) {
94
94
  const file = path.join(__dirname, '..', 'errors', new Date().toISOString()), stage = token.getAttribute('stage');
95
+ for (const k in config) {
96
+ if (k.startsWith('regex')) {
97
+ delete config[k];
98
+ }
99
+ }
95
100
  fs.writeFileSync(file, stage === constants_1.MAX_STAGE ? wikitext : token.toString());
96
101
  fs.writeFileSync(`${file}.err`, e.stack);
97
102
  fs.writeFileSync(`${file}.json`, JSON.stringify({ stage, include, config }, null, '\t'));
@@ -10,7 +10,7 @@ const closes = {
10
10
  '{': String.raw `\}{2,}|\|`,
11
11
  '-': String.raw `\}-`,
12
12
  '[': String.raw `\]\]`,
13
- }, marks = new Map([['!', '!'], ['!!', '+'], ['(!', '{'], ['!)', '}'], ['!-', '-'], ['=', '~']]), re = new RegExp(String.raw `\{\{\s*(${[...marks.keys()].map(string_1.escapeRegExp).join('|')})\s*\}\}(?!\})`, 'gu');
13
+ }, marks = new Map([['!', '!'], ['!!', '+'], ['(!', '{'], ['!)', '}'], ['!-', '-'], ['=', '~'], ['server', 'm']]), re = /\{\{\s*([^\s\0<>[\]{}|_#&%:.]+)\s*\}\}(?!\})/gu;
14
14
  /**
15
15
  * 解析花括号
16
16
  * @param wikitext
@@ -23,7 +23,7 @@ const parseBraces = (wikitext, config, accum) => {
23
23
  wikitext = wikitext.replace(re, (m, p1) => {
24
24
  // @ts-expect-error abstract class
25
25
  new transclude_1.TranscludeToken(m.slice(2, -2), [], config, accum);
26
- return `\0${accum.length - 2}${marks.get(p1)}\x7F`;
26
+ return `\0${accum.length - 2}${marks.get(p1.toLowerCase()) ?? 't'}\x7F`;
27
27
  });
28
28
  const lastBraces = wikitext.lastIndexOf('}}') - wikitext.length;
29
29
  let moreBraces = lastBraces + wikitext.length !== -1, regex = new RegExp(source + (moreBraces ? openBraces : ''), 'gmu'), mt = regex.exec(wikitext), lastIndex;
@@ -87,10 +87,10 @@ const parseBraces = (wikitext, config, accum) => {
87
87
  // @ts-expect-error abstract class
88
88
  new transclude_1.TranscludeToken(parts[0][0], parts.slice(1), config, accum);
89
89
  const name = (0, string_1.removeComment)(parts[0][0]).trim();
90
- if (marks.has(name)) {
91
- ch = marks.get(name); // 标记{{!}}等
90
+ if (marks.has(name.toLowerCase())) {
91
+ ch = marks.get(name.toLowerCase()); // 标记{{!}}等
92
92
  }
93
- else if (/^(?:filepath|(?:full|canonical)urle?):.|^server$/iu.test(name)) {
93
+ else if (/^(?:filepath|(?:full|canonical)urle?):./iu.test(name)) {
94
94
  ch = 'm';
95
95
  }
96
96
  else if (/^#vardefine:./iu.test(name)) {
@@ -56,7 +56,7 @@ const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
56
56
  if (name) {
57
57
  ch = 'e';
58
58
  // @ts-expect-error abstract class
59
- new ext_1.ExtToken(name, attr, inner, closing, config, accum);
59
+ new ext_1.ExtToken(name, attr, inner, closing, config, include, accum);
60
60
  }
61
61
  else if (substr.startsWith('<!--')) {
62
62
  ch = 'c';
@@ -9,12 +9,14 @@ const converter_1 = require("../src/converter");
9
9
  * @param accum
10
10
  */
11
11
  const parseConverter = (text, config, accum) => {
12
- const variants = `(?:${config.variants.join('|')})`, regex1 = /-\{/gu, regex2 = /-\{|\}-/gu, regex3 = new RegExp(String.raw `;(?=(?:[^;]*?=>)?\s*${variants}\s*:|(?:\s|\0\d+[cn]\x7F)*$)`, 'u'), stack = [];
12
+ config.regexConverter ??= new RegExp(String.raw `;(?=(?:[^;]*?=>)?\s*(?:${config.variants.join('|')})\s*:|(?:\s|\0\d+[cn]\x7F)*$)`, 'u');
13
+ const regex1 = /-\{/gu, regex2 = /-\{|\}-/gu, stack = [];
13
14
  let regex = regex1, mt = regex.exec(text);
14
15
  while (mt) {
15
16
  const { 0: syntax, index } = mt;
16
17
  if (syntax === '}-') {
17
- const top = stack.pop(), { length } = accum, str = text.slice(top.index + 2, index), i = str.indexOf('|'), [flags, raw] = i === -1 ? [[], str] : [str.slice(0, i).split(';'), str.slice(i + 1)], temp = raw.replace(/(&[#a-z\d]+);/giu, '$1\x01'), rules = temp.split(regex3).map(rule => rule.replace(/\x01/gu, ';'));
18
+ const top = stack.pop(), { length } = accum, str = text.slice(top.index + 2, index), i = str.indexOf('|'), [flags, raw] = i === -1 ? [[], str] : [str.slice(0, i).split(';'), str.slice(i + 1)], temp = raw.replace(/(&[#a-z\d]+);/giu, '$1\x01'), rules = temp.split(config.regexConverter)
19
+ .map(rule => rule.replace(/\x01/gu, ';'));
18
20
  // @ts-expect-error abstract class
19
21
  new converter_1.ConverterToken(flags, rules, config, accum);
20
22
  text = `${text.slice(0, top.index)}\0${length}v\x7F${text.slice(index + 2)}`;
@@ -12,8 +12,8 @@ const magicLink_1 = require("../src/magicLink");
12
12
  * @param inFile 是否在图链中
13
13
  */
14
14
  const parseExternalLinks = (wikitext, config, accum, inFile) => {
15
- const regex = new RegExp(String.raw `\[(\0\d+f\x7F|(?:(?:${config.protocol}|//)${string_1.extUrlCharFirst}|\0\d+m\x7F)${string_1.extUrlChar}(?=[[\]<>"\t\p{Zs}]|\0\d))(\p{Zs}*(?!\p{Zs}))([^\]\x01-\x08\x0A-\x1F\uFFFD]*)\]`, 'giu');
16
- return wikitext.replace(regex, (_, url, space, text) => {
15
+ config.regexExternalLinks ??= new RegExp(String.raw `\[(\0\d+f\x7F|(?:(?:${config.protocol}|//)${string_1.extUrlCharFirst}|\0\d+m\x7F)${string_1.extUrlChar}(?=[[\]<>"\t\p{Zs}]|\0\d))(\p{Zs}*(?!\p{Zs}))([^\]\x01-\x08\x0A-\x1F\uFFFD]*)\]`, 'giu');
16
+ return wikitext.replace(config.regexExternalLinks, (_, url, space, text) => {
17
17
  const { length } = accum, mt = /&[lg]t;/u.exec(url);
18
18
  if (mt) {
19
19
  url = url.slice(0, mt.index);
@@ -12,6 +12,7 @@ const heading_1 = require("../src/heading");
12
12
  */
13
13
  const parseHrAndDoubleUnderscore = ({ firstChild: { data }, type, name }, config, accum) => {
14
14
  const { doubleUnderscore } = config, insensitive = new Set(doubleUnderscore[0]), sensitive = new Set(doubleUnderscore[1]);
15
+ config.regexHrAndDoubleUnderscore ??= new RegExp(`__(${[...insensitive, ...sensitive].join('|')})__`, 'giu');
15
16
  if (type !== 'root' && (type !== 'ext-inner' || name !== 'poem')) {
16
17
  data = `\0${data}`;
17
18
  }
@@ -19,7 +20,7 @@ const parseHrAndDoubleUnderscore = ({ firstChild: { data }, type, name }, config
19
20
  // @ts-expect-error abstract class
20
21
  new hr_1.HrToken(m, config, accum);
21
22
  return `${lead}\0${accum.length - 1}r\x7F`;
22
- }).replace(new RegExp(`__(${[...insensitive, ...sensitive].join('|')})__`, 'giu'), (m, p1) => {
23
+ }).replace(config.regexHrAndDoubleUnderscore, (m, p1) => {
23
24
  const caseSensitive = sensitive.has(p1), lc = p1.toLowerCase(), caseInsensitive = insensitive.has(lc);
24
25
  if (caseSensitive || caseInsensitive) {
25
26
  // @ts-expect-error abstract class
@@ -7,6 +7,7 @@ const externalLinks_1 = require("./externalLinks");
7
7
  const index_2 = require("../src/link/index");
8
8
  const file_1 = require("../src/link/file");
9
9
  const category_1 = require("../src/link/category");
10
+ const regexImg = /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(\||\0\d+!\x7F)(.*)$/su;
10
11
  /**
11
12
  * 解析内部链接
12
13
  * @param wikitext
@@ -14,9 +15,10 @@ const category_1 = require("../src/link/category");
14
15
  * @param accum
15
16
  */
16
17
  const parseLinks = (wikitext, config, accum) => {
18
+ config.regexLinks ??= new RegExp(String.raw `^\s*(?:${config.protocol}|//)`, 'iu');
17
19
  const regex = true // eslint-disable-line no-constant-condition, @typescript-eslint/no-unnecessary-condition
18
20
  ? /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(?:(\||\0\d+!\x7F)(.*?[^\]]))?\]\](.*)$/su
19
- : /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(?:(\||\0\d+!\x7F)(.*?[^\]])?)?\]\](.*)$/su, regexImg = /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(\||\0\d+!\x7F)(.*)$/su, regexExt = new RegExp(String.raw `^\s*(?:${config.protocol}|//)`, 'iu'), bits = wikitext.split('[[');
21
+ : /^((?:(?!\0\d+!\x7F)[^\n[\]{}|])+)(?:(\||\0\d+!\x7F)(.*?[^\]])?)?\]\](.*)$/su, bits = wikitext.split('[[');
20
22
  let s = bits.shift();
21
23
  for (let i = 0; i < bits.length; i++) {
22
24
  let mightBeImg = false, link, delimiter, text, after;
@@ -35,7 +37,7 @@ const parseLinks = (wikitext, config, accum) => {
35
37
  [, link, delimiter, text] = m2;
36
38
  }
37
39
  }
38
- if (link === undefined || regexExt.test(link) || /\0\d+[exhbru]\x7F/u.test(link)) {
40
+ if (link === undefined || config.regexLinks.test(link) || /\0\d+[exhbru]\x7F/u.test(link)) {
39
41
  s += `[[${x}`;
40
42
  continue;
41
43
  }
@@ -11,8 +11,8 @@ const space = String.raw `[\p{Zs}\t]|&nbsp;|&#0*160;|&#x0*a0;`, sp = `(?:${space
11
11
  * @param accum
12
12
  */
13
13
  const parseMagicLinks = (wikitext, config, accum) => {
14
- const regex = new RegExp(String.raw `(^|[^\p{L}\d_])(?:(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})|${magicLinkPattern})`, 'giu');
15
- return wikitext.replace(regex, (m, lead, p1) => {
14
+ config.regexMagicLinks ??= new RegExp(String.raw `(^|[^\p{L}\d_])(?:(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})|${magicLinkPattern})`, 'giu');
15
+ return wikitext.replace(config.regexMagicLinks, (m, lead, p1) => {
16
16
  let url = lead ? m.slice(lead.length) : m;
17
17
  if (p1) {
18
18
  let trail = '';
@@ -10,7 +10,8 @@ const redirect_1 = require("../src/redirect");
10
10
  * @param accum
11
11
  */
12
12
  const parseRedirect = (text, config, accum) => {
13
- const re = new RegExp(String.raw `^(\s*)((?:${config.redirection.join('|')})\s*(?::\s*)?)\[\[([^\n|\]]+)(\|.*?)?\]\](\s*)`, 'iu'), mt = re.exec(text);
13
+ config.regexRedirect ??= new RegExp(String.raw `^(\s*)((?:${config.redirection.join('|')})\s*(?::\s*)?)\[\[([^\n|\]]+)(\|.*?)?\]\](\s*)`, 'iu');
14
+ const mt = config.regexRedirect.exec(text);
14
15
  if (mt && index_1.default.normalizeTitle(mt[3], 0, false, config, true, true).valid) {
15
16
  text = `\0${accum.length}o\x7F${text.slice(mt[0].length)}`;
16
17
  // @ts-expect-error abstract class
@@ -3,9 +3,11 @@ import { Token } from './index';
3
3
  import { AtomToken } from './atom';
4
4
  import { AttributeToken } from './attribute';
5
5
  import type { LintError } from '../base';
6
- import type { ExtToken, HtmlToken, TdToken, TrToken, TableToken, SyntaxToken } from '../internal';
6
+ import type { ExtToken, HtmlToken, SyntaxToken } from '../internal';
7
7
  import type { AttributeTypes } from './attribute';
8
+ import type { TableTokens } from './table/index';
8
9
  declare type AttributesTypes = `${AttributeTypes}s`;
10
+ declare type Child = AtomToken | AttributeToken;
9
11
  /**
10
12
  * 扩展和HTML标签属性
11
13
  * @classdesc `{childNodes: ...AtomToken|AttributeToken}`
@@ -13,10 +15,10 @@ declare type AttributesTypes = `${AttributeTypes}s`;
13
15
  export declare abstract class AttributesToken extends Token {
14
16
  #private;
15
17
  readonly name: string;
16
- readonly childNodes: readonly (AtomToken | AttributeToken)[];
17
- abstract get firstChild(): AtomToken | AttributeToken | undefined;
18
- abstract get lastChild(): AtomToken | AttributeToken | undefined;
19
- abstract get parentNode(): ExtToken | HtmlToken | TableToken | TrToken | TdToken | undefined;
18
+ readonly childNodes: readonly Child[];
19
+ abstract get firstChild(): Child | undefined;
20
+ abstract get lastChild(): Child | undefined;
21
+ abstract get parentNode(): ExtToken | HtmlToken | TableTokens | undefined;
20
22
  abstract get previousSibling(): SyntaxToken | undefined;
21
23
  get type(): AttributesTypes;
22
24
  /**
@@ -4,6 +4,7 @@ import { GalleryImageToken } from './link/galleryImage';
4
4
  import { NoincludeToken } from './nowiki/noinclude';
5
5
  import type { LintError } from '../base';
6
6
  import type { AstText, AttributesToken, ExtToken } from '../internal';
7
+ declare type Child = GalleryImageToken | NoincludeToken;
7
8
  /**
8
9
  * gallery标签
9
10
  * @classdesc `{childNodes: ...(GalleryImageToken|NoincludeToken|AstText)}`
@@ -11,9 +12,9 @@ import type { AstText, AttributesToken, ExtToken } from '../internal';
11
12
  export declare abstract class GalleryToken extends Token {
12
13
  #private;
13
14
  readonly name: 'gallery';
14
- readonly childNodes: readonly (GalleryImageToken | NoincludeToken | AstText)[];
15
- abstract get firstChild(): GalleryImageToken | NoincludeToken | AstText | undefined;
16
- abstract get lastChild(): GalleryImageToken | NoincludeToken | AstText | undefined;
15
+ readonly childNodes: readonly (Child | AstText)[];
16
+ abstract get firstChild(): Child | AstText | undefined;
17
+ abstract get lastChild(): Child | AstText | undefined;
17
18
  abstract get nextSibling(): undefined;
18
19
  abstract get previousSibling(): AttributesToken;
19
20
  abstract get parentNode(): ExtToken | undefined;
@@ -21,3 +22,4 @@ export declare abstract class GalleryToken extends Token {
21
22
  /** @param inner 标签内部wikitext */
22
23
  constructor(inner?: string, config?: Parser.Config, accum?: Token[]);
23
24
  }
25
+ export {};
@@ -5,15 +5,16 @@ import { GalleryImageToken } from './link/galleryImage';
5
5
  import { ImagemapLinkToken } from './imagemapLink';
6
6
  import type { LintError } from '../base';
7
7
  import type { AstText, AttributesToken, ExtToken } from '../internal';
8
+ declare type Child = GalleryImageToken | NoincludeToken;
8
9
  /**
9
10
  * `<imagemap>`
10
11
  * @classdesc `{childNodes: ...NoincludeToken, GalleryImageToken, ...(NoincludeToken|ImagemapLinkToken|AstText)}`
11
12
  */
12
13
  export declare abstract class ImagemapToken extends Token {
13
14
  readonly name: 'imagemap';
14
- readonly childNodes: readonly (GalleryImageToken | NoincludeToken | ImagemapLinkToken | AstText)[];
15
- abstract get firstChild(): NoincludeToken | GalleryImageToken | undefined;
16
- abstract get lastChild(): GalleryImageToken | NoincludeToken | ImagemapLinkToken | AstText | undefined;
15
+ readonly childNodes: readonly (Child | ImagemapLinkToken | AstText)[];
16
+ abstract get firstChild(): Child | undefined;
17
+ abstract get lastChild(): Child | ImagemapLinkToken | AstText | undefined;
17
18
  abstract get nextSibling(): undefined;
18
19
  abstract get previousSibling(): AttributesToken;
19
20
  abstract get parentNode(): ExtToken | undefined;
@@ -23,3 +24,4 @@ export declare abstract class ImagemapToken extends Token {
23
24
  /** @param inner 标签内部wikitext */
24
25
  constructor(inner?: string, config?: Parser.Config, accum?: Token[]);
25
26
  }
27
+ export {};
@@ -2,18 +2,19 @@ import Parser from '../index';
2
2
  import { Token } from './index';
3
3
  import { ExtToken } from './tagPair/ext';
4
4
  import { NoincludeToken } from './nowiki/noinclude';
5
- import { CommentToken } from './nowiki/comment';
6
5
  import type { LintError } from '../base';
7
- import type { AttributesToken } from './attributes';
6
+ import type { CommentToken, AttributesToken, IncludeToken, ArgToken, TranscludeToken } from '../internal';
7
+ declare type Child = ExtToken | NoincludeToken | CommentToken | IncludeToken | ArgToken | TranscludeToken;
8
8
  /**
9
9
  * 嵌套式的扩展标签
10
10
  * @classdesc `{childNodes: ...ExtToken|NoincludeToken|CommentToken}`
11
11
  */
12
12
  export declare abstract class NestedToken extends Token {
13
+ #private;
13
14
  readonly name: string;
14
- readonly childNodes: readonly (ExtToken | NoincludeToken | CommentToken)[];
15
- abstract get firstChild(): ExtToken | NoincludeToken | CommentToken | undefined;
16
- abstract get lastChild(): ExtToken | NoincludeToken | CommentToken | undefined;
15
+ readonly childNodes: readonly Child[];
16
+ abstract get firstChild(): Child | undefined;
17
+ abstract get lastChild(): Child | undefined;
17
18
  abstract get nextSibling(): undefined;
18
19
  abstract get previousSibling(): AttributesToken;
19
20
  abstract get parentNode(): ExtToken | undefined;
@@ -22,5 +23,6 @@ export declare abstract class NestedToken extends Token {
22
23
  * @param regex 内层正则
23
24
  * @param tags 内层标签名
24
25
  */
25
- constructor(wikitext: string | undefined, regex: RegExp, tags: readonly string[], config?: Parser.Config, accum?: Token[]);
26
+ constructor(wikitext: string | undefined, regex: RegExp | boolean, tags: readonly string[], config?: Parser.Config, accum?: Token[]);
26
27
  }
28
+ export {};
@@ -3,16 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NestedToken = void 0;
4
4
  const lint_1 = require("../util/lint");
5
5
  const rect_1 = require("../lib/rect");
6
+ const commentAndExt_1 = require("../parser/commentAndExt");
7
+ const braces_1 = require("../parser/braces");
6
8
  const index_1 = require("../index");
7
9
  const index_2 = require("./index");
8
10
  const ext_1 = require("./tagPair/ext");
9
11
  const noinclude_1 = require("./nowiki/noinclude");
10
- const comment_1 = require("./nowiki/comment");
12
+ const childTypes = new Set(['comment', 'include', 'arg', 'template', 'magic-word']);
11
13
  /**
12
14
  * 嵌套式的扩展标签
13
15
  * @classdesc `{childNodes: ...ExtToken|NoincludeToken|CommentToken}`
14
16
  */
15
17
  class NestedToken extends index_2.Token {
18
+ #tags;
19
+ #regex;
16
20
  get type() {
17
21
  return 'ext-inner';
18
22
  }
@@ -21,36 +25,46 @@ class NestedToken extends index_2.Token {
21
25
  * @param tags 内层标签名
22
26
  */
23
27
  constructor(wikitext, regex, tags, config = index_1.default.getConfig(), accum = []) {
24
- wikitext = wikitext?.replace(regex, (comment, name, attr, inner, closing) => {
25
- const str = `\0${accum.length + 1}${name ? 'e' : 'c'}\x7F`;
26
- if (name) {
28
+ if (typeof regex === 'boolean') {
29
+ const placeholder = Symbol('InputboxToken'), { length } = accum;
30
+ accum.push(placeholder);
31
+ wikitext &&= (0, commentAndExt_1.parseCommentAndExt)(wikitext, config, accum, regex);
32
+ wikitext &&= (0, braces_1.parseBraces)(wikitext, config, accum);
33
+ accum.splice(length, 1);
34
+ }
35
+ else {
36
+ wikitext &&= wikitext.replace(regex, (_, name, attr, inner, closing) => {
37
+ const str = `\0${accum.length + 1}e\x7F`;
27
38
  // @ts-expect-error abstract class
28
- new ext_1.ExtToken(name, attr, inner, closing, config, accum);
29
- }
30
- else {
31
- const closed = comment.endsWith('-->');
32
- // @ts-expect-error abstract class
33
- new comment_1.CommentToken(comment.slice(4, closed ? -3 : undefined), closed, config, accum);
34
- }
35
- return str;
36
- }).replace(/(^|\0\d+[cne]\x7F)([^\0]+)(?=$|\0\d+[cne]\x7F)/gu, (_, lead, substr) => {
39
+ new ext_1.ExtToken(name, attr, inner, closing, config, false, accum);
40
+ return str;
41
+ });
42
+ }
43
+ wikitext &&= wikitext.replace(/(^|\0\d+.\x7F)([^\0]+)(?=$|\0\d+.\x7F)/gu, (_, lead, substr) => {
37
44
  // @ts-expect-error abstract class
38
45
  new noinclude_1.NoincludeToken(substr, config, accum);
39
46
  return `${lead}\0${accum.length}n\x7F`;
40
47
  });
41
- super(wikitext, config, accum, {});
48
+ super(wikitext, config, accum);
49
+ this.#tags = [...tags];
50
+ this.#regex = regex;
42
51
  }
43
52
  /** @private */
44
53
  lint(start = this.getAbsoluteIndex(), re) {
45
- const rect = new rect_1.BoundingRect(this, start);
54
+ const rect = new rect_1.BoundingRect(this, start), noinclude = this.#regex ? 'includeonly' : 'noinclude', regex = typeof this.#regex === 'boolean'
55
+ ? new RegExp(String.raw `^(?:<${noinclude}(?:\s[^>]*)?/?>|</${noinclude}\s*>)$`, 'iu')
56
+ : /^<!--.*-->$/su;
46
57
  return [
47
58
  ...super.lint(start, re),
48
59
  ...this.childNodes.filter(child => {
49
- if (child.type === 'ext' || child.type === 'comment') {
60
+ if (child.type === 'ext') {
61
+ return !this.#tags.includes(child.name);
62
+ }
63
+ else if (childTypes.has(child.type)) {
50
64
  return false;
51
65
  }
52
66
  const str = child.toString().trim();
53
- return str && !/^<!--.*-->$/su.test(str);
67
+ return str && !regex.test(str);
54
68
  }).map(child => {
55
69
  const e = (0, lint_1.generateForChild)(child, rect, 'no-ignored', index_1.default.msg('invalid content in <$1>', this.name));
56
70
  e.suggestions = [
@@ -17,5 +17,5 @@ export declare abstract class ParamTagToken extends Token {
17
17
  abstract get parentNode(): ExtToken | undefined;
18
18
  get type(): 'ext-inner';
19
19
  /** @class */
20
- constructor(wikitext?: string, config?: Parser.Config, accum?: Token[], acceptable?: Acceptable);
20
+ constructor(include: boolean, wikitext?: string, config?: Parser.Config, accum?: Token[], acceptable?: Acceptable);
21
21
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ParamTagToken = void 0;
4
4
  const lint_1 = require("../../util/lint");
5
5
  const rect_1 = require("../../lib/rect");
6
+ const commentAndExt_1 = require("../../parser/commentAndExt");
6
7
  const index_1 = require("../../index");
7
8
  const index_2 = require("../index");
8
9
  const atom_1 = require("../atom");
@@ -15,12 +16,16 @@ class ParamTagToken extends index_2.Token {
15
16
  return 'ext-inner';
16
17
  }
17
18
  /** @class */
18
- constructor(wikitext, config = index_1.default.getConfig(), accum = [], acceptable = {}) {
19
+ constructor(include, wikitext, config = index_1.default.getConfig(), accum = [], acceptable) {
19
20
  super(undefined, config, accum, {});
20
21
  if (wikitext) {
21
22
  const SingleLineAtomToken = atom_1.AtomToken;
22
- this.append(...wikitext.split('\n').map(line => new SingleLineAtomToken(line, 'param-line', config, accum, {})));
23
+ this.append(...wikitext.split('\n')
24
+ .map(line => acceptable ? line : (0, commentAndExt_1.parseCommentAndExt)(line, config, accum, include))
25
+ .map(line => new SingleLineAtomToken(line, 'param-line', config, accum, {})));
23
26
  }
27
+ accum.splice(accum.indexOf(this), 1);
28
+ accum.push(this);
24
29
  }
25
30
  /** @private */
26
31
  toString(skip) {
@@ -36,21 +41,33 @@ class ParamTagToken extends index_2.Token {
36
41
  }
37
42
  /** @private */
38
43
  lint(start = this.getAbsoluteIndex()) {
39
- const rect = new rect_1.BoundingRect(this, start);
40
- return this.childNodes.filter(child => {
41
- const { childNodes } = child, i = childNodes.findIndex(({ type }) => type !== 'text'), str = (i >= 0 ? childNodes.slice(0, i).map(String).join('') : child.toString()).trim();
42
- return str && !(i >= 0 ? /^[a-z]+(?:\[\])?\s*(?:=|$)/iu : /^[a-z]+(?:\[\])?\s*=/iu).test(str);
43
- }).map(child => {
44
- const e = (0, lint_1.generateForChild)(child, rect, 'no-ignored', index_1.default.msg('invalid parameter of <$1>', this.name));
45
- e.suggestions = [
46
- {
47
- desc: 'remove',
48
- range: [e.startIndex, e.endIndex],
49
- text: '',
50
- },
51
- ];
52
- return e;
53
- });
44
+ const rect = new rect_1.BoundingRect(this, start), msg = index_1.default.msg('invalid parameter of <$1>', this.name), errors = [];
45
+ for (const child of this.childNodes) {
46
+ const grandChildren = child.childNodes
47
+ .filter(({ type }) => type !== 'comment' && type !== 'include' && type !== 'noinclude');
48
+ if (grandChildren.some(({ type }) => type === 'ext')) {
49
+ errors.push((0, lint_1.generateForChild)(child, rect, 'no-ignored', msg));
50
+ }
51
+ else {
52
+ const i = grandChildren.findIndex(({ type }) => type !== 'text'), str = grandChildren.slice(0, i >= 0 ? i : undefined).map(String).join('');
53
+ if (str && !(i >= 0 ? /^[a-z]+(?:\[\])?\s*(?:=|$)/iu : /^[a-z]+(?:\[\])?\s*=/iu).test(str)) {
54
+ const e = (0, lint_1.generateForChild)(child, rect, 'no-ignored', msg);
55
+ e.suggestions = [
56
+ {
57
+ desc: 'remove',
58
+ range: [e.startIndex, e.endIndex],
59
+ text: '',
60
+ },
61
+ ];
62
+ errors.push(e);
63
+ }
64
+ else {
65
+ errors.push(...child.lint(start, false));
66
+ }
67
+ }
68
+ start += child.toString().length + 1;
69
+ }
70
+ return errors;
54
71
  }
55
72
  }
56
73
  exports.ParamTagToken = ParamTagToken;
@@ -4,5 +4,5 @@ import type { Token } from '../index';
4
4
  /** `<inputbox>` */
5
5
  export declare abstract class InputboxToken extends ParamTagToken {
6
6
  /** @class */
7
- constructor(wikitext?: string, config?: Parser.Config, accum?: Token[]);
7
+ constructor(include: boolean, wikitext?: string, config?: Parser.Config, accum?: Token[]);
8
8
  }
@@ -1,18 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.InputboxToken = void 0;
4
+ const commentAndExt_1 = require("../../parser/commentAndExt");
4
5
  const braces_1 = require("../../parser/braces");
5
6
  const index_1 = require("../../index");
6
7
  const index_2 = require("./index");
7
8
  /** `<inputbox>` */
8
9
  class InputboxToken extends index_2.ParamTagToken {
9
10
  /** @class */
10
- constructor(wikitext, config = index_1.default.getConfig(), accum = []) {
11
+ constructor(include, wikitext, config = index_1.default.getConfig(), accum = []) {
11
12
  const placeholder = Symbol('InputboxToken'), { length } = accum;
12
13
  accum.push(placeholder);
14
+ wikitext &&= (0, commentAndExt_1.parseCommentAndExt)(wikitext, config, accum, include);
13
15
  wikitext &&= (0, braces_1.parseBraces)(wikitext, config, accum);
14
16
  accum.splice(length, 1);
15
- super(wikitext, config, accum, {});
17
+ super(include, wikitext, config, accum, {});
16
18
  }
17
19
  }
18
20
  exports.InputboxToken = InputboxToken;
package/dist/src/pre.d.ts CHANGED
@@ -3,15 +3,16 @@ import { Token } from './index';
3
3
  import { NoincludeToken } from './nowiki/noinclude';
4
4
  import type { LintError } from '../base';
5
5
  import type { AstText, AttributesToken, ExtToken, ConverterToken } from '../internal';
6
+ declare type Child = NoincludeToken | ConverterToken;
6
7
  /**
7
8
  * `<pre>`
8
9
  * @classdesc `{childNodes: [...AstText|NoincludeToken|ConverterToken]}`
9
10
  */
10
11
  export declare abstract class PreToken extends Token {
11
12
  readonly name: 'pre';
12
- readonly childNodes: readonly (AstText | NoincludeToken | ConverterToken)[];
13
- abstract get firstChild(): AstText | NoincludeToken | ConverterToken | undefined;
14
- abstract get lastChild(): AstText | NoincludeToken | ConverterToken | undefined;
13
+ readonly childNodes: readonly (AstText | Child)[];
14
+ abstract get firstChild(): AstText | Child | undefined;
15
+ abstract get lastChild(): AstText | Child | undefined;
15
16
  abstract get nextSibling(): undefined;
16
17
  abstract get previousSibling(): AttributesToken;
17
18
  abstract get parentNode(): ExtToken | undefined;
@@ -19,3 +20,4 @@ export declare abstract class PreToken extends Token {
19
20
  /** @class */
20
21
  constructor(wikitext?: string, config?: Parser.Config, accum?: Token[]);
21
22
  }
23
+ export {};
package/dist/src/pre.js CHANGED
@@ -16,19 +16,27 @@ class PreToken extends index_2.Token {
16
16
  /** @class */
17
17
  constructor(wikitext, config = index_1.default.getConfig(), accum = []) {
18
18
  if (wikitext) {
19
- const opening = '<nowiki>', closing = '</nowiki>', { length } = opening;
20
- let i = wikitext.indexOf(opening), j = wikitext.indexOf(closing, i + length), str = '';
21
- while (i !== -1 && j !== -1) {
19
+ const opening = /<nowiki>/giu, closing = /<\/nowiki>/giu, { length } = opening.source;
20
+ let i = opening.exec(wikitext);
21
+ if (i) {
22
+ closing.lastIndex = i.index + length;
23
+ }
24
+ let j = closing.exec(wikitext), lastIndex = 0, str = '';
25
+ while (i && j) {
22
26
  // @ts-expect-error abstract class
23
- new noinclude_1.NoincludeToken(opening, config, accum);
27
+ new noinclude_1.NoincludeToken(i[0], config, accum);
24
28
  // @ts-expect-error abstract class
25
- new noinclude_1.NoincludeToken(closing, config, accum);
26
- str += `${wikitext.slice(0, i)}\0${accum.length - 1}n\x7F${wikitext.slice(i + length, j)}\0${accum.length}n\x7F`;
27
- wikitext = wikitext.slice(j + length + 1);
28
- i = wikitext.indexOf(opening);
29
- j = wikitext.indexOf(closing, i + length);
29
+ new noinclude_1.NoincludeToken(j[0], config, accum);
30
+ str += `${wikitext.slice(lastIndex, i.index)}\0${accum.length - 1}n\x7F${wikitext.slice(i.index + length, j.index)}\0${accum.length}n\x7F`;
31
+ lastIndex = j.index + length + 1;
32
+ opening.lastIndex = lastIndex;
33
+ i = opening.exec(wikitext);
34
+ if (i) {
35
+ closing.lastIndex = i.index + length;
36
+ }
37
+ j = closing.exec(wikitext);
30
38
  }
31
- wikitext = str + wikitext;
39
+ wikitext = str + wikitext.slice(lastIndex);
32
40
  }
33
41
  super(wikitext, config, accum, {});
34
42
  this.setAttribute('stage', constants_1.MAX_STAGE - 1);
@@ -17,7 +17,7 @@ class TableBaseToken extends (0, attributesParent_1.attributesParent)(1)(index_2
17
17
  * @param type 节点类型
18
18
  * @param attr 表格属性
19
19
  */
20
- constructor(pattern, syntax, type, attr, config = index_1.default.getConfig(), accum = [], acceptable = {}) {
20
+ constructor(pattern, syntax, type, attr, config = index_1.default.getConfig(), accum = [], acceptable) {
21
21
  super(undefined, config, accum, acceptable);
22
22
  this.append(new syntax_1.SyntaxToken(syntax, pattern, 'table-syntax', config, accum, {}),
23
23
  // @ts-expect-error abstract class
@@ -3,6 +3,7 @@ import { SyntaxToken } from '../syntax';
3
3
  import type { Config, LintError } from '../../base';
4
4
  import type { AttributesToken, TdToken, TrToken, Token } from '../../internal';
5
5
  import type { TableCoords } from './trBase';
6
+ export type TableTokens = TableToken | TrToken | TdToken;
6
7
  /**
7
8
  * 是否是行尾
8
9
  * @param {Token} cell 表格单元格
@@ -15,9 +15,10 @@ export declare abstract class ExtToken extends TagPairToken {
15
15
  get type(): 'ext';
16
16
  /**
17
17
  * @param name 标签名
18
+ * @param include 是否嵌入
18
19
  * @param attr 标签属性
19
20
  * @param inner 内部wikitext
20
21
  * @param closed 是否封闭
21
22
  */
22
- constructor(name: string, attr?: string, inner?: string, closed?: string, config?: Parser.Config, accum?: Token[]);
23
+ constructor(name: string, attr?: string, inner?: string, closed?: string, config?: Parser.Config, include?: boolean, accum?: Token[]);
23
24
  }
@@ -33,11 +33,12 @@ class ExtToken extends index_3.TagPairToken {
33
33
  }
34
34
  /**
35
35
  * @param name 标签名
36
+ * @param include 是否嵌入
36
37
  * @param attr 标签属性
37
38
  * @param inner 内部wikitext
38
39
  * @param closed 是否封闭
39
40
  */
40
- constructor(name, attr, inner, closed, config = index_1.default.getConfig(), accum = []) {
41
+ constructor(name, attr, inner, closed, config = index_1.default.getConfig(), include = false, accum = []) {
41
42
  const lcName = name.toLowerCase(),
42
43
  // @ts-expect-error abstract class
43
44
  attrToken = new attributes_1.AttributesToken(!attr || attr.trimStart() !== attr ? attr : ` ${attr}`, 'ext-attrs', lcName, config, accum), newConfig = { ...config, ext: del(config.ext, lcName), excludes: [...config.excludes ?? []] };
@@ -65,17 +66,18 @@ class ExtToken extends index_3.TagPairToken {
65
66
  break;
66
67
  case 'dynamicpagelist':
67
68
  // @ts-expect-error abstract class
68
- innerToken = new index_4.ParamTagToken(inner, newConfig, accum);
69
+ innerToken = new index_4.ParamTagToken(include, inner, newConfig, accum);
69
70
  break;
70
71
  case 'inputbox':
71
72
  newConfig.excludes.push('heading');
72
73
  // @ts-expect-error abstract class
73
- innerToken = new inputbox_1.InputboxToken(inner, newConfig, accum);
74
+ innerToken = new inputbox_1.InputboxToken(include, inner, newConfig, accum);
74
75
  break;
75
76
  case 'references': {
76
77
  const { NestedToken } = require('../nested');
78
+ newConfig.excludes.push('heading');
77
79
  // @ts-expect-error abstract class
78
- innerToken = new NestedToken(inner, /<!--.*?(?:-->|$)|<(ref)(\s[^>]*?)?(?:\/>|>(.*?)<\/(ref\s*)>)/gisu, ['ref'], newConfig, accum);
80
+ innerToken = new NestedToken(inner, include, ['ref'], newConfig, accum);
79
81
  break;
80
82
  }
81
83
  case 'choose': {
@@ -36,6 +36,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
37
  exports.IncludeToken = void 0;
38
38
  const lint_1 = require("../../util/lint");
39
+ const rect_1 = require("../../lib/rect");
39
40
  const hidden_1 = require("../../mixin/hidden");
40
41
  const index_1 = require("../../index");
41
42
  const index_2 = require("./index");
@@ -76,18 +77,30 @@ let IncludeToken = (() => {
76
77
  }
77
78
  /** @private */
78
79
  lint(start = this.getAbsoluteIndex()) {
79
- if (this.closed) {
80
- return [];
80
+ const errors = [], { firstChild, closed, name } = this, rect = new rect_1.BoundingRect(this, start);
81
+ if (firstChild.data.trim()) {
82
+ const e = (0, lint_1.generateForChild)(firstChild, rect, 'no-ignored', 'useless attribute', 'warning');
83
+ e.suggestions = [
84
+ {
85
+ desc: 'remove',
86
+ range: [e.startIndex, e.endIndex],
87
+ text: '',
88
+ },
89
+ ];
90
+ errors.push(e);
81
91
  }
82
- const e = (0, lint_1.generateForSelf)(this, { start }, 'unclosed-comment', index_1.default.msg('unclosed $1', `<${this.name}>`));
83
- e.suggestions = [
84
- {
85
- desc: 'close',
86
- range: [e.endIndex, e.endIndex],
87
- text: `</${this.name}>`,
88
- },
89
- ];
90
- return [e];
92
+ if (!closed) {
93
+ const e = (0, lint_1.generateForSelf)(this, rect, 'unclosed-comment', index_1.default.msg('unclosed $1', `<${name}>`));
94
+ e.suggestions = [
95
+ {
96
+ desc: 'close',
97
+ range: [e.endIndex, e.endIndex],
98
+ text: `</${name}>`,
99
+ },
100
+ ];
101
+ errors.push(e);
102
+ }
103
+ return errors;
91
104
  }
92
105
  };
93
106
  return IncludeToken = _classThis;
@@ -4,6 +4,7 @@ import { ParameterToken } from './parameter';
4
4
  import { AtomToken } from './atom';
5
5
  import { SyntaxToken } from './syntax';
6
6
  import type { LintError } from '../base';
7
+ declare type Child = AtomToken | SyntaxToken;
7
8
  /**
8
9
  * 模板或魔术字
9
10
  * @classdesc `{childNodes: [AtomToken|SyntaxToken, ...AtomToken, ...ParameterToken]}`
@@ -11,9 +12,9 @@ import type { LintError } from '../base';
11
12
  export declare abstract class TranscludeToken extends Token {
12
13
  #private;
13
14
  readonly modifier: string;
14
- readonly childNodes: readonly [AtomToken | SyntaxToken, ...ParameterToken[]] | readonly [SyntaxToken, AtomToken, AtomToken, ...ParameterToken[]];
15
- abstract get firstChild(): AtomToken | SyntaxToken;
16
- abstract get lastChild(): AtomToken | SyntaxToken | ParameterToken;
15
+ readonly childNodes: readonly [Child, ...ParameterToken[]] | readonly [SyntaxToken, AtomToken, AtomToken, ...ParameterToken[]];
16
+ abstract get firstChild(): Child;
17
+ abstract get lastChild(): Child | ParameterToken;
17
18
  get type(): 'template' | 'magic-word';
18
19
  /**
19
20
  * @param title 模板标题或魔术字
@@ -55,3 +56,4 @@ export declare abstract class TranscludeToken extends Token {
55
56
  */
56
57
  getPossibleValues(): Token[];
57
58
  }
59
+ export {};
@@ -37,6 +37,12 @@ class TranscludeToken extends index_2.Token {
37
37
  * @throws `SyntaxError` 非法的模板名称
38
38
  */
39
39
  constructor(title, parts, config = index_1.default.getConfig(), accum = []) {
40
+ let heading;
41
+ const m = /^(?:\s|\0\d+[cn]\x7F)*\0(\d+)h\x7F(?:\s|\0\d+[cn]\x7F)*/u.exec(title);
42
+ if (m) {
43
+ heading = Number(m[1]);
44
+ title = title.replace(`\0${heading}h\x7F`, accum[heading].toString().replace(/^\n/u, ''));
45
+ }
40
46
  super(undefined, config, accum, {});
41
47
  const { parserFunction: [insensitive, sensitive] } = config, argSubst = /^(?:\s|\0\d+[cn]\x7F)*\0\d+s\x7F/u.exec(title)?.[0];
42
48
  if (argSubst) {
@@ -81,6 +87,10 @@ class TranscludeToken extends index_2.Token {
81
87
  const token = new atom_1.AtomToken(title, 'template-name', config, accum, {});
82
88
  super.insertAt(token);
83
89
  }
90
+ if (typeof heading === 'number') {
91
+ // @ts-expect-error sparse array
92
+ accum[heading] = undefined;
93
+ }
84
94
  const templateLike = this.isTemplate();
85
95
  let i = 1;
86
96
  for (const [j, part] of parts.entries()) {
package/i18n/zh-hans.json CHANGED
@@ -53,6 +53,7 @@
53
53
  "unexpected template argument": "未预期的模板参数",
54
54
  "unmatched closing tag": "未匹配的闭合标签",
55
55
  "unnecessary URL encoding in an internal link": "内链中不必要的URL编码",
56
+ "useless attribute": "无用的属性",
56
57
  "useless fragment": "无用的fragment",
57
58
  "useless link text": "无用的链接文字",
58
59
  "variable anchor in a section header": "段落标题中可变的锚点",
package/i18n/zh-hant.json CHANGED
@@ -53,6 +53,7 @@
53
53
  "unexpected template argument": "未預期的模板參數",
54
54
  "unmatched closing tag": "未匹配的閉合標籤",
55
55
  "unnecessary URL encoding in an internal link": "內部連結中不必要的URL編碼",
56
+ "useless attribute": "無用的屬性",
56
57
  "useless fragment": "無用的fragment",
57
58
  "useless link text": "無用的連結文字",
58
59
  "variable anchor in a section header": "段落標題中可變的錨點",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikilint",
3
- "version": "2.13.0",
3
+ "version": "2.13.2",
4
4
  "description": "A Node.js linter for MediaWiki markup",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -51,7 +51,7 @@
51
51
  "chalk": "^4.1.2"
52
52
  },
53
53
  "devDependencies": {
54
- "@bhsd/common": "^0.1.1",
54
+ "@bhsd/common": "^0.3.0",
55
55
  "@types/node": "^20.11.6",
56
56
  "v8r": "^3.0.0"
57
57
  },