wikilint 2.3.5 → 2.3.7

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.
@@ -19,7 +19,7 @@ class AstElement extends node_1.AstNode {
19
19
  }
20
20
  /** 合并相邻的文本子节点 */
21
21
  normalize() {
22
- const { childNodes } = this;
22
+ const childNodes = [...this.childNodes];
23
23
  for (let i = childNodes.length - 1; i >= 0; i--) {
24
24
  const { type, data } = childNodes[i], prev = childNodes[i - 1];
25
25
  if (type !== 'text' || this.getGaps(i - 1)) {
@@ -33,6 +33,7 @@ class AstElement extends node_1.AstNode {
33
33
  childNodes.splice(i, 1);
34
34
  }
35
35
  }
36
+ this.setAttribute('childNodes', childNodes);
36
37
  }
37
38
  /**
38
39
  * 移除子节点
@@ -81,7 +82,9 @@ class AstElement extends node_1.AstNode {
81
82
  * @param elements 新的子节点
82
83
  */
83
84
  replaceChildren(...elements) {
84
- this.childNodes.length = 0;
85
+ for (let i = this.length - 1; i >= 0; i--) {
86
+ this.removeAt(i);
87
+ }
85
88
  this.append(...elements);
86
89
  }
87
90
  /**
@@ -90,7 +93,8 @@ class AstElement extends node_1.AstNode {
90
93
  * @param i 子节点位置
91
94
  */
92
95
  setText(str, i = 0) {
93
- const oldText = this.childNodes.at(i);
96
+ i += i < 0 ? this.length : 0;
97
+ const oldText = this.childNodes[i];
94
98
  const { data } = oldText;
95
99
  oldText.replaceData(str);
96
100
  return data;
package/dist/lib/node.js CHANGED
@@ -11,7 +11,7 @@ class AstNode {
11
11
  }
12
12
  /** 末位子节点 */
13
13
  get lastChild() {
14
- return this.childNodes.at(-1);
14
+ return this.childNodes[this.childNodes.length - 1];
15
15
  }
16
16
  /** 父节点 */
17
17
  get parentNode() {
@@ -68,21 +68,15 @@ class AstNode {
68
68
  posFromIndex(index) {
69
69
  const str = String(this);
70
70
  if (index >= -str.length && index <= str.length) {
71
- const lines = str.slice(0, index).split('\n');
72
- return {
73
- top: lines.length - 1,
74
- left: lines.at(-1).length,
75
- };
71
+ const lines = str.slice(0, index).split('\n'), top = lines.length - 1;
72
+ return { top, left: lines[top].length };
76
73
  }
77
74
  return undefined;
78
75
  }
79
76
  /** 获取行数和最后一行的列数 */
80
77
  #getDimension() {
81
- const lines = String(this).split('\n');
82
- return {
83
- height: lines.length,
84
- width: lines.at(-1).length,
85
- };
78
+ const lines = String(this).split('\n'), height = lines.length;
79
+ return { height, width: lines[height - 1].length };
86
80
  }
87
81
  /** @private */
88
82
  getGaps(i) {
package/dist/lib/text.js CHANGED
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AstText = void 0;
4
4
  const Parser = require("../index");
5
5
  const node_1 = require("./node");
6
- const errorSyntax = /<\s*\/?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*\])|(?<=^|\])([^[]*?)\]+|\]{2,}|https?[:/]\/+/giu, errorSyntaxUrl = /<\s*\/?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*\])|(?<=^|\])([^[]*?)\]+|\]{2,}/giu, disallowedTags = [
6
+ const errorSyntax = /<\s*\/?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*\])|((?:^|\])[^[]*?)\]+|https?[:/]\/+/giu, errorSyntaxUrl = /<\s*\/?([a-z]\w*)|\{+|\}+|\[{2,}|\[(?![^[]*\])|((?:^|\])[^[]*?)\]+/giu, disallowedTags = [
7
7
  'html',
8
8
  'head',
9
9
  'style',
@@ -37,7 +37,11 @@ class AstText extends node_1.AstNode {
37
37
  /** @param text 包含文本 */
38
38
  constructor(text) {
39
39
  super();
40
- this.data = text;
40
+ Object.defineProperties(this, {
41
+ data: {
42
+ value: text,
43
+ },
44
+ });
41
45
  }
42
46
  /** @private */
43
47
  toString() {
@@ -68,17 +72,19 @@ class AstText extends node_1.AstNode {
68
72
  else {
69
73
  errorRegex = errorSyntax;
70
74
  }
71
- const errors = [...data.matchAll(errorRegex)], { ext, html } = this.getRootNode().getAttribute('config');
72
- if (errors.length > 0) {
75
+ const errors = [], { ext, html } = this.getRootNode().getAttribute('config');
76
+ if (data.search(errorRegex) !== -1) {
77
+ errorRegex.lastIndex = 0;
73
78
  const root = this.getRootNode(), { top, left } = root.posFromIndex(start), tags = new Set(['onlyinclude', 'noinclude', 'includeonly', ext, html, disallowedTags].flat(2));
74
- return errors
75
- .map(({ 0: error, 1: tag, 2: prefix, index }) => {
76
- if (prefix) {
79
+ for (let mt = errorRegex.exec(data); mt; mt = errorRegex.exec(data)) {
80
+ const [, tag, prefix] = mt;
81
+ let { 0: error, index } = mt;
82
+ if (prefix && prefix !== ']') {
77
83
  const { length } = prefix;
78
84
  index += length;
79
85
  error = error.slice(length);
80
86
  }
81
- const startIndex = start + index, lines = data.slice(0, index).split('\n'), startLine = lines.length + top - 1, line = lines.at(-1), startCol = lines.length === 1 ? left + line.length : line.length, { 0: char, length } = error, endIndex = startIndex + length, rootStr = String(root), nextChar = rootStr[endIndex], previousChar = rootStr[startIndex - 1], severity = length > 1 && (char !== '<' || /[\s/>]/u.test(nextChar ?? ''))
87
+ const startIndex = start + index, lines = data.slice(0, index).split('\n'), startLine = lines.length + top - 1, line = lines[lines.length - 1], startCol = lines.length === 1 ? left + line.length : line.length, { 0: char, length } = error, endIndex = startIndex + length, rootStr = String(root), nextChar = rootStr[endIndex], previousChar = rootStr[startIndex - 1], severity = length > 1 && (char !== '<' || /[\s/>]/u.test(nextChar ?? ''))
82
88
  || char === '{' && (nextChar === char || previousChar === '-')
83
89
  || char === '}' && (previousChar === char || nextChar === '-')
84
90
  || char === '[' && (nextChar === char
@@ -88,17 +94,20 @@ class AstText extends node_1.AstNode {
88
94
  || !data.slice(0, index).trim() && previousType === 'free-ext-link')
89
95
  ? 'error'
90
96
  : 'warning';
91
- return (char !== '<' || tags.has(tag.toLowerCase())) && {
92
- message: Parser.msg('lonely "$1"', char === 'h' ? error : char),
93
- severity,
94
- startIndex,
95
- endIndex,
96
- startLine,
97
- endLine: startLine,
98
- startCol,
99
- endCol: startCol + length,
100
- };
101
- }).filter(Boolean);
97
+ if (char !== '<' || tags.has(tag.toLowerCase())) {
98
+ errors.push({
99
+ message: Parser.msg('lonely "$1"', char === 'h' ? error : char),
100
+ severity,
101
+ startIndex,
102
+ endIndex,
103
+ startLine,
104
+ endLine: startLine,
105
+ startCol,
106
+ endCol: startCol + length,
107
+ });
108
+ }
109
+ }
110
+ return errors;
102
111
  }
103
112
  return [];
104
113
  }
package/dist/lib/title.js CHANGED
@@ -27,7 +27,7 @@ class Title {
27
27
  }
28
28
  catch { }
29
29
  }
30
- title = title.replaceAll('_', ' ').trim();
30
+ title = title.replace(/_/gu, ' ').trim();
31
31
  let ns = defaultNs;
32
32
  if (title.startsWith(':')) {
33
33
  ns = 0;
@@ -17,7 +17,7 @@ const parseBraces = (wikitext, config = Parser.getConfig(), accum = []) => {
17
17
  const source = `${config.excludes?.includes('heading') ? '' : '^(\0\\d+c\x7F)*={1,6}|'}\\[\\[|\\{{2,}|-\\{(?!\\{)`, { parserFunction: [, , , subst] } = config, stack = [], closes = { '=': '\n', '{': '\\}{2,}|\\|', '-': '\\}-', '[': '\\]\\]' }, marks = new Map([['!', '!'], ['!!', '+'], ['(!', '{'], ['!)', '}'], ['!-', '-'], ['=', '~']]);
18
18
  let regex = new RegExp(source, 'gmu'), mt = regex.exec(wikitext), moreBraces = wikitext.includes('}}'), lastIndex;
19
19
  while (mt
20
- || lastIndex !== undefined && lastIndex <= wikitext.length && stack.at(-1)?.[0]?.startsWith('=')) {
20
+ || lastIndex !== undefined && lastIndex <= wikitext.length && stack[stack.length - 1]?.[0]?.startsWith('=')) {
21
21
  if (mt?.[1]) {
22
22
  const [, { length }] = mt;
23
23
  mt[0] = mt[0].slice(length);
@@ -29,14 +29,14 @@ const parseBraces = (wikitext, config = Parser.getConfig(), accum = []) => {
29
29
  * @param text wikitext全文
30
30
  */
31
31
  const push = (text) => {
32
- parts.at(-1).push(text.slice(topPos, curIndex));
32
+ parts[parts.length - 1].push(text.slice(topPos, curIndex));
33
33
  };
34
34
  if (syntax === ']]' || syntax === '}-') { // 情形1:闭合内链或转换
35
35
  lastIndex = curIndex + 2;
36
36
  }
37
37
  else if (syntax === '\n') { // 情形2:闭合标题或文末
38
38
  lastIndex = curIndex + 1;
39
- const { pos, findEqual } = stack.at(-1) ?? {};
39
+ const { pos, findEqual } = stack[stack.length - 1] ?? {};
40
40
  if (pos === undefined || findEqual || (0, string_1.removeComment)(wikitext.slice(pos, index)) !== '') {
41
41
  const rmt = /^(={1,6})(.+)\1((?:\s|\0\d+c\x7F)*)$/u
42
42
  .exec(wikitext.slice(index, curIndex));
@@ -112,10 +112,10 @@ const parseBraces = (wikitext, config = Parser.getConfig(), accum = []) => {
112
112
  stack.push(...'0' in top ? [top] : [], mt);
113
113
  }
114
114
  moreBraces &&= wikitext.slice(lastIndex).includes('}}');
115
- let curTop = stack.at(-1);
115
+ let curTop = stack[stack.length - 1];
116
116
  if (!moreBraces && curTop?.[0]?.startsWith('{')) {
117
117
  stack.pop();
118
- curTop = stack.at(-1);
118
+ curTop = stack[stack.length - 1];
119
119
  }
120
120
  regex = new RegExp(source + (curTop ? `|${closes[curTop[0][0]]}${curTop.findEqual ? '|=' : ''}` : ''), 'gmu');
121
121
  regex.lastIndex = lastIndex;
@@ -49,9 +49,12 @@ const parseCommentAndExt = (wikitext, config = Parser.getConfig(), accum = [], i
49
49
  return str;
50
50
  }
51
51
  }
52
- const ext = config.ext.join('|'), noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly', regex = new RegExp('<!--.*?(?:-->|$)|' // comment
53
- + `<${noincludeRegex}(?:\\s[^>]*?)?>|</${noincludeRegex}\\s*>|` // <noinclude>
54
- + `<(${ext})(\\s[^>]*?)?(?:/>|>(.*?)</(\\1\\s*)>)|` // 扩展标签
52
+ const ext = config.ext.join('|'), noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly', regex = new RegExp('<!--.*?(?:-->|$)' // comment
53
+ + '|'
54
+ + `<${noincludeRegex}(?:\\s[^>]*?)?>|</${noincludeRegex}\\s*>` // <noinclude>
55
+ + '|'
56
+ + `<(${ext})(\\s[^>]*?)?(?:/>|>(.*?)</(\\1\\s*)>)` // 扩展标签
57
+ + '|'
55
58
  + `<(${includeRegex})(\\s[^>]*?)?(?:/>|>(.*?)(?:</(${includeRegex}\\s*)>|$))`, // <includeonly>
56
59
  'gisu');
57
60
  return wikitext.replace(regex, (substr, name, attr, inner, closing, include, includeAttr, includeInner, includeClosing) => {
@@ -15,10 +15,10 @@ const parseConverter = (text, config = Parser.getConfig(), accum = []) => {
15
15
  while (mt) {
16
16
  const { 0: syntax, index } = mt;
17
17
  if (syntax === '}-') {
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)],
19
- // eslint-disable-next-line regexp/prefer-lookaround
20
- temp = raw.replace(/(&[#a-z\d]+);/giu, '$1\x01'), variants = `(?:${config.variants.join('|')})`, rules = temp.split(new RegExp(`;(?=\\s*(?:${variants}|[^;]*?=>\\s*${variants})\\s*:)`, 'u'))
21
- .map(rule => rule.replaceAll('\x01', ';'));
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'), variants = `(?:${config.variants.join('|')})`, rules = temp.split(new RegExp(`;(?=\\s*(?:${`(?:${variants}|[^;]*?=>\\s*${variants})\\s*:`
19
+ + '|'
20
+ + '(?:\0\\d+c\x7f(?:\\s|\0\\d+c\x7f)*)?$' // 末尾的空白
21
+ }))`, 'u')).map(rule => rule.replace(/\x01/gu, ';'));
22
22
  new converter_1.ConverterToken(flags, rules, config, accum);
23
23
  text = `${text.slice(0, top.index)}\0${length}v\x7F${text.slice(index + 2)}`;
24
24
  if (stack.length === 0) {
@@ -11,9 +11,9 @@ const magicLink_1 = require("../src/magicLink");
11
11
  * @param accum
12
12
  */
13
13
  const parseMagicLinks = (wikitext, config = Parser.getConfig(), accum = []) => {
14
- const regex = new RegExp(`(?<![\\p{L}\\d_])(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})`, 'giu');
15
- return wikitext.replace(regex, (m, p1) => {
16
- let trail = '', url = m;
14
+ const regex = new RegExp(`(^|[^\\p{L}\\d_])(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})`, 'giu');
15
+ return wikitext.replace(regex, (m, lead, p1) => {
16
+ let trail = '', url = lead ? m.slice(1) : m;
17
17
  const m2 = /&(?:lt|gt|nbsp|#x0*(?:3[ce]|a0)|#0*(?:6[02]|160));/iu.exec(url);
18
18
  if (m2) {
19
19
  trail = url.slice(m2.index);
@@ -32,7 +32,7 @@ const parseMagicLinks = (wikitext, config = Parser.getConfig(), accum = []) => {
32
32
  return m;
33
33
  }
34
34
  new magicLink_1.MagicLinkToken(url, false, config, accum);
35
- return `\0${accum.length - 1}w\x7F${trail}`;
35
+ return `${lead}\0${accum.length - 1}w\x7F${trail}`;
36
36
  });
37
37
  };
38
38
  exports.parseMagicLinks = parseMagicLinks;
@@ -35,7 +35,7 @@ const parseQuotes = (wikitext, config = Parser.getConfig(), accum = []) => {
35
35
  firstSpace = i;
36
36
  }
37
37
  }
38
- else if (arr[i - 1].at(-2) === ' ') {
38
+ else if (arr[i - 1].slice(-2, -1) === ' ') {
39
39
  firstSingle = i;
40
40
  }
41
41
  else {
@@ -80,7 +80,7 @@ const parseTable = ({ firstChild: { data }, type, name }, config = Parser.getCon
80
80
  top = stack.pop();
81
81
  }
82
82
  top.close(`\n${spaces}${closing}`, true);
83
- push(attr, stack.at(-1));
83
+ push(attr, stack[stack.length - 1]);
84
84
  }
85
85
  else if (row) {
86
86
  top = pop();
@@ -112,7 +112,13 @@ const commonHtmlAttrs = new Set([
112
112
  ]),
113
113
  tabs: new Set(['plain', 'class', 'container', 'id', 'title', 'style']),
114
114
  combobox: new Set(['placeholder', 'value', 'id', 'class', 'text', 'dropdown', 'style']),
115
- }, insecureStyle = new RegExp(`${'expression'}|${'(?:filter|accelerator|-o-link(?:-source)?|-o-replace)\\s*:'}|${'(?:url|image(?:-set)?)\\s*\\('}|${'attr\\s*\\([^)]+[\\s,]url'}`, 'u');
115
+ }, insecureStyle = new RegExp('expression'
116
+ + '|'
117
+ + '(?:filter|accelerator|-o-link(?:-source)?|-o-replace)\\s*:'
118
+ + '|'
119
+ + '(?:url|image(?:-set)?)\\s*\\('
120
+ + '|'
121
+ + 'attr\\s*\\([^)]+[\\s,]url', 'u');
116
122
  /**
117
123
  * 扩展和HTML标签属性
118
124
  * @classdesc `{childNodes: [AtomToken, Token|AtomToken]}`
@@ -172,7 +178,7 @@ class AttributeToken extends index_1.Token {
172
178
  /** @private */
173
179
  afterBuild() {
174
180
  if (this.#equal.includes('\0')) {
175
- this.#equal = this.buildFromStr(this.#equal, 'string');
181
+ this.#equal = this.buildFromStr(this.#equal, constants_1.BuildMethod.String);
176
182
  }
177
183
  if (this.parentNode) {
178
184
  this.setAttribute('tag', this.parentNode.name);
@@ -233,7 +239,7 @@ class AttributeToken extends index_1.Token {
233
239
  if (this.#quotes[1]) {
234
240
  return value;
235
241
  }
236
- return this.#quotes[0] ? value.trimEnd() : value.trim();
242
+ return value[this.#quotes[0] ? 'trimEnd' : 'trim']();
237
243
  }
238
244
  return this.type === 'ext-attr' || '';
239
245
  }
@@ -33,12 +33,11 @@ class AttributesToken extends index_1.Token {
33
33
  this.setAttribute('name', name);
34
34
  if (attr) {
35
35
  const regex = new RegExp('([^\\s/](?:(?!\0\\d+~\x7F)[^\\s/=])*)' // 属性名
36
- + '(?:'
37
- + '((?:\\s|\0\\d+c\x7F)*' // `=`前的空白字符
38
- + '(?:=|\0\\d+~\x7F)' // `=`
39
- + '(?:\\s|\0\\d+c\x7F)*)' // `=`后的空白字符
40
- + `(?:(["'])(.*?)(\\3|$)|(\\S*))` // 属性值
41
- + ')?', 'gsu');
36
+ + `(?:${'((?:\\s|\0\\d+c\x7F)*' // `=`前的空白字符
37
+ + '(?:=|\0\\d+~\x7F)' // `=`
38
+ + '(?:\\s|\0\\d+c\x7F)*)' // `=`后的空白字符
39
+ + `(?:(["'])(.*?)(\\3|$)|(\\S*))` // 属性值
40
+ })?`, 'gsu');
42
41
  let out = '', mt = regex.exec(attr), lastIndex = 0;
43
42
  const insertDirty = /** 插入无效属性 */ () => {
44
43
  if (out) {
@@ -84,7 +83,7 @@ class AttributesToken extends index_1.Token {
84
83
  */
85
84
  getAttrToken(key) {
86
85
  const tokens = this.getAttrTokens(key);
87
- return tokens.at(-1);
86
+ return tokens[tokens.length - 1];
88
87
  }
89
88
  /**
90
89
  * 获取指定属性
@@ -1,3 +1,4 @@
1
+ import { BuildMethod } from '../util/constants';
1
2
  import * as Parser from '../index';
2
3
  import { AstElement } from '../lib/element';
3
4
  import { AstText } from '../lib/text';
package/dist/src/index.js CHANGED
@@ -127,15 +127,15 @@ class Token extends element_1.AstElement {
127
127
  if (i % 2 === 0) {
128
128
  return new text_1.AstText(s);
129
129
  }
130
- else if (Number.isNaN(Number(s.at(-1)))) {
130
+ else if (Number.isNaN(Number(s.slice(-1)))) {
131
131
  return this.#accum[Number(s.slice(0, -1))];
132
132
  }
133
133
  throw new Error(`解析错误!未正确标记的 Token:${s}`);
134
134
  });
135
- if (type === 'string') {
135
+ if (type === constants_1.BuildMethod.String) {
136
136
  return nodes.map(String).join('');
137
137
  }
138
- else if (type === 'text') {
138
+ else if (type === constants_1.BuildMethod.Text) {
139
139
  return (0, string_1.text)(nodes);
140
140
  }
141
141
  return nodes;
@@ -279,10 +279,7 @@ class Token extends element_1.AstElement {
279
279
  return this.#include;
280
280
  }
281
281
  const root = this.getRootNode();
282
- if (root !== this) {
283
- return root.getAttribute('include');
284
- }
285
- return false;
282
+ return (root !== this && root.getAttribute('include'));
286
283
  }
287
284
  default:
288
285
  return super.getAttribute(key);
@@ -34,7 +34,7 @@ class LinkBaseToken extends index_1.Token {
34
34
  afterBuild() {
35
35
  this.#title = this.getTitle();
36
36
  if (this.#delimiter.includes('\0')) {
37
- this.#delimiter = this.buildFromStr(this.#delimiter, 'string');
37
+ this.#delimiter = this.buildFromStr(this.#delimiter, constants_1.BuildMethod.String);
38
38
  }
39
39
  }
40
40
  /** @private */
@@ -28,9 +28,10 @@ class MagicLinkToken extends index_1.Token {
28
28
  }
29
29
  rect ??= { start, ...this.getRootNode().posFromIndex(start) };
30
30
  const refError = (0, lint_1.generateForChild)(child, rect, '', 'warning');
31
- errors.push(...[...data.matchAll(regexGlobal)].map(({ index, 0: s }) => {
32
- const lines = data.slice(0, index).split('\n'), { length: top } = lines, { length: left } = lines.at(-1), startIndex = refError.startIndex + index, startLine = refError.startLine + top - 1, startCol = top === 1 ? refError.startCol + left : left;
33
- return {
31
+ regexGlobal.lastIndex = 0;
32
+ for (let mt = regexGlobal.exec(data); mt; mt = regexGlobal.exec(data)) {
33
+ const { index, 0: s } = mt, lines = data.slice(0, index).split('\n'), { length: top } = lines, { length: left } = lines[lines.length - 1], startIndex = refError.startIndex + index, startLine = refError.startLine + top - 1, startCol = top === 1 ? refError.startCol + left : left;
34
+ errors.push({
34
35
  ...refError,
35
36
  message: Parser.msg('$1 in URL', s.startsWith('|') ? '"|"' : Parser.msg('full-width punctuation')),
36
37
  startIndex,
@@ -39,8 +40,8 @@ class MagicLinkToken extends index_1.Token {
39
40
  endLine: startLine,
40
41
  startCol,
41
42
  endCol: startCol + s.length,
42
- };
43
- }));
43
+ });
44
+ }
44
45
  }
45
46
  return errors;
46
47
  }
@@ -28,9 +28,9 @@ class NestedToken extends index_1.Token {
28
28
  new comment_1.CommentToken(comment.slice(4, closed ? -3 : undefined), closed, config, accum);
29
29
  }
30
30
  return str;
31
- })?.replace(/(?<=^|\0\d+[ce]\x7F)[^\0]+(?=$|\0\d+[ce]\x7F)/gu, substr => {
31
+ })?.replace(/(^|\0\d+[ce]\x7F)([^\0]+)(?=$|\0\d+[ce]\x7F)/gu, (_, lead, substr) => {
32
32
  new noinclude_1.NoincludeToken(substr, config, accum);
33
- return `\0${accum.length}c\x7F`;
33
+ return `${lead}\0${accum.length}c\x7F`;
34
34
  });
35
35
  super(wikitext, config, accum, {});
36
36
  }
@@ -14,7 +14,7 @@ class QuoteToken extends base_1.NowikiBaseToken {
14
14
  let refError;
15
15
  if (previousSibling?.type === 'text' && previousSibling.data.endsWith(`'`)) {
16
16
  refError = (0, lint_1.generateForSelf)(this, { start }, message);
17
- const { startIndex: endIndex, startLine: endLine, startCol: endCol } = refError, [{ length }] = /(?<!')'+$/u.exec(previousSibling.data), startIndex = start - length;
17
+ const { startIndex: endIndex, startLine: endLine, startCol: endCol } = refError, [, { length }] = /(?:^|[^'])('+)$/u.exec(previousSibling.data), startIndex = start - length;
18
18
  errors.push({
19
19
  ...refError,
20
20
  startIndex,
@@ -10,7 +10,7 @@ const index_1 = require("./index");
10
10
  * @param name 预定的参数名
11
11
  */
12
12
  const getName = (name) => name.toString(new Set(['comment', 'noinclude', 'include']))
13
- .replace(/^[ \t\n\0\v]+|(?<=[^ \t\n\0\v])[ \t\n\0\v]+$/gu, '');
13
+ .replace(/^[ \t\n\0\v]+|([^ \t\n\0\v])[ \t\n\0\v]+$/gu, '$1');
14
14
  /**
15
15
  * 模板或魔术字参数
16
16
  * @classdesc `{childNodes: [Token, Token]}`
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TdToken = void 0;
4
4
  const lint_1 = require("../../util/lint");
5
+ const constants_1 = require("../../util/constants");
5
6
  const Parser = require("../../index");
6
7
  const index_1 = require("../index");
7
8
  const base_1 = require("./base");
@@ -38,7 +39,7 @@ class TdToken extends base_1.TableBaseToken {
38
39
  }
39
40
  /** 表格语法信息 */
40
41
  #getSyntax() {
41
- const syntax = this.firstChild.text(), char = syntax.at(-1);
42
+ const syntax = this.firstChild.text(), char = syntax.slice(-1);
42
43
  let subtype = 'td';
43
44
  if (char === '!') {
44
45
  subtype = 'th';
@@ -51,7 +52,7 @@ class TdToken extends base_1.TableBaseToken {
51
52
  /** @private */
52
53
  afterBuild() {
53
54
  if (this.#innerSyntax.includes('\0')) {
54
- this.#innerSyntax = this.buildFromStr(this.#innerSyntax, 'string');
55
+ this.#innerSyntax = this.buildFromStr(this.#innerSyntax, constants_1.BuildMethod.String);
55
56
  }
56
57
  }
57
58
  /** @private */
@@ -76,8 +77,11 @@ class TdToken extends base_1.TableBaseToken {
76
77
  const errors = super.lint(start);
77
78
  start += this.getRelativeIndex(this.length - 1);
78
79
  for (const child of this.lastChild.childNodes) {
79
- if (child.type === 'text' && child.data.includes('|')) {
80
- errors.push((0, lint_1.generateForChild)(child, { start }, 'additional "|" in a table cell', 'warning'));
80
+ if (child.type === 'text') {
81
+ const { data } = child;
82
+ if (data.includes('|')) {
83
+ errors.push((0, lint_1.generateForChild)(child, { start }, 'additional "|" in a table cell', data.includes('||') ? 'error' : 'warning'));
84
+ }
81
85
  }
82
86
  }
83
87
  return errors;
@@ -29,9 +29,7 @@ class ExtToken extends index_2.TagPairToken {
29
29
  * @param closed 是否封闭
30
30
  */
31
31
  constructor(name, attr, inner, closed, config = Parser.getConfig(), accum = []) {
32
- const lcName = name.toLowerCase(), attrToken = new attributes_1.AttributesToken(!attr || attr.trimStart() !== attr
33
- ? attr
34
- : ` ${attr}`, 'ext-attrs', lcName, config, accum), newConfig = { ...config, ext: del(config.ext, lcName), excludes: [...config.excludes ?? []] };
32
+ const lcName = name.toLowerCase(), 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 ?? []] };
35
33
  let innerToken;
36
34
  switch (lcName) {
37
35
  case 'tab':
@@ -4,6 +4,7 @@ exports.TranscludeToken = void 0;
4
4
  const string_1 = require("../util/string");
5
5
  const lint_1 = require("../util/lint");
6
6
  const debug_1 = require("../util/debug");
7
+ const constants_1 = require("../util/constants");
7
8
  const Parser = require("../index");
8
9
  const index_1 = require("./index");
9
10
  const parameter_1 = require("./parameter");
@@ -122,7 +123,7 @@ class TranscludeToken extends index_1.Token {
122
123
  /** @private */
123
124
  afterBuild() {
124
125
  if (this.modifier.includes('\0')) {
125
- this.setAttribute('modifier', this.buildFromStr(this.modifier, 'string'));
126
+ this.setAttribute('modifier', this.buildFromStr(this.modifier, constants_1.BuildMethod.String));
126
127
  }
127
128
  }
128
129
  /** @private */
@@ -230,7 +231,7 @@ class TranscludeToken extends index_1.Token {
230
231
  * @param copy 是否返回一个备份
231
232
  */
232
233
  getArgs(key, exact = false, copy = true) {
233
- const keyStr = String(key).replace(/^[ \t\n\0\v]+|(?<=[^ \t\n\0\v])[ \t\n\0\v]+$/gu, '');
234
+ const keyStr = String(key).replace(/^[ \t\n\0\v]+|([^ \t\n\0\v])[ \t\n\0\v]+$/gu, '$1');
234
235
  let args;
235
236
  if (this.#args.has(keyStr)) {
236
237
  args = this.#args.get(keyStr);
@@ -1,4 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MAX_STAGE = void 0;
3
+ exports.BuildMethod = exports.MAX_STAGE = void 0;
4
4
  exports.MAX_STAGE = 11;
5
+ var BuildMethod;
6
+ (function (BuildMethod) {
7
+ BuildMethod[BuildMethod["String"] = 0] = "String";
8
+ BuildMethod[BuildMethod["Text"] = 1] = "Text";
9
+ })(BuildMethod || (exports.BuildMethod = BuildMethod = {}));
@@ -22,7 +22,8 @@ exports.isToken = isToken;
22
22
  * @param inserted 插入的子节点
23
23
  */
24
24
  const setChildNodes = (parent, position, deleteCount, inserted = []) => {
25
- const { childNodes } = parent, removed = childNodes.splice(position, deleteCount, ...inserted);
25
+ const childNodes = [...parent.childNodes], removed = childNodes.splice(position, deleteCount, ...inserted);
26
+ parent.setAttribute('childNodes', childNodes);
26
27
  for (const node of inserted) {
27
28
  node.setAttribute('parentNode', parent);
28
29
  }
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.noWrap = exports.decodeHtml = exports.text = exports.escapeRegExp = exports.removeComment = exports.extUrlChar = exports.extUrlCharFirst = void 0;
4
- exports.extUrlCharFirst = '(?:\\[[\\da-f:.]+\\]|[^[\\]<>"\\0-\\x1F\\x7F\\p{Zs}\\uFFFD])';
5
- exports.extUrlChar = '(?:[^[\\]<>"\\0-\\x1F\\x7F\\p{Zs}\\uFFFD]|\\0\\d+[c!~]\\x7F)*';
4
+ exports.extUrlCharFirst = '(?:\\[[\\da-f:.]+\\]|[^[\\]<>"\0-\x1F\x7F\\p{Zs}\uFFFD])';
5
+ exports.extUrlChar = '(?:[^[\\]<>"\0-\x1F\x7F\\p{Zs}\uFFFD]|\0\\d+[c!~]\x7F)*';
6
6
  /**
7
7
  * 生成正则替换函数
8
8
  * @param regex 正则表达式
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikilint",
3
- "version": "2.3.5",
3
+ "version": "2.3.7",
4
4
  "description": "A Node.js linter for MediaWiki markup",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -19,7 +19,8 @@
19
19
  "/config/",
20
20
  "/i18n/",
21
21
  "/bin/cli",
22
- "/dist/"
22
+ "/dist/",
23
+ "!/dist/test/"
23
24
  ],
24
25
  "bin": {
25
26
  "wikilint": "./bin/cli.js"
@@ -30,15 +31,15 @@
30
31
  "url": "git+https://github.com/bhsd-harry/wikiparser-node.git"
31
32
  },
32
33
  "scripts": {
33
- "prepublishOnly": "npm run build; rm dist/internal.js dist/base.js dist/[bmpu]*/*.d.ts; rm -r dist/test",
34
- "build": "rm -rf dist/; tsc; grep -rl --include='*.d.ts' '@private' dist/ | xargs gsed -i '/@private/,+1d'",
34
+ "prepublishOnly": "npm run build && rm dist/internal.js dist/base.js dist/[bmpu]*/*.d.ts",
35
+ "build": "rm -rf dist/; tsc && grep -rl --include='*.d.ts' '@private' dist/ | xargs gsed -i '/@private/,+1d'",
35
36
  "diff": "git diff --ignore-all-space --color-moved",
36
37
  "lint:ts": "tsc --noEmit && eslint --cache .",
37
38
  "lint:json": "ajv -s config/.schema.json -d 'config/*.json' --strict=true --strict-required=false",
38
39
  "lint": "npm run lint:ts && npm run lint:json",
39
40
  "test": "npx wikilint test/single-page.wiki",
40
41
  "test:real": "node dist/test/real.js",
41
- "test:single": "node dist/test/single.js; node --prof dist/test/single.js && node --prof-process isolate-0x*-v8.log > test/processed.txt && rm isolate-0x*-v8.log"
42
+ "test:single": "node dist/test/single.js && node --prof dist/test/single.js && node --prof-process isolate-0x*-v8.log > test/processed.txt && rm isolate-0x*-v8.log"
42
43
  },
43
44
  "devDependencies": {
44
45
  "@cypress/request": "^3.0.1",
@@ -49,6 +50,7 @@
49
50
  "@typescript-eslint/parser": "^6.12.0",
50
51
  "ajv-cli": "^5.0.0",
51
52
  "eslint": "^8.56.0",
53
+ "eslint-plugin-es-x": "^7.3.0",
52
54
  "eslint-plugin-eslint-comments": "^3.2.0",
53
55
  "eslint-plugin-jsdoc": "^47.0.2",
54
56
  "eslint-plugin-json-es": "^1.5.7",