wikilint 2.48.1 → 2.48.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -97,6 +97,6 @@ repos:
97
97
  # You can add any CLI options as needed
98
98
  entry: wikilint
99
99
  language: node
100
- additional_dependencies: ['wikilint@2.48.1']
100
+ additional_dependencies: ['wikilint@2.48.2']
101
101
  files: \.(mediawiki|wiki|wikitext)$
102
102
  ```
@@ -41,7 +41,7 @@ const filterGadget = (id) => {
41
41
  const n = Number(id);
42
42
  return n < 2300 || n > 2303; // Gadget, Gadget talk, Gadget definition, Gadget definition talk
43
43
  };
44
- const pkg = 'wikilint', version = '2.48.1';
44
+ const pkg = 'wikilint', version = '2.48.2';
45
45
  /**
46
46
  * Get the parser configuration for a Wikimedia Foundation project.
47
47
  * @param site site nickname
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ const path_1 = __importDefault(require("path"));
16
16
  const common_1 = require("@bhsd/common");
17
17
  const diff_1 = require("./util/diff");
18
18
  /* NOT FOR BROWSER ONLY */
19
- const re = new RegExp(String.raw `^https?:\/\/([^./]+)\.(${common_1.wmf})\.org`, 'iu');
19
+ const re = new RegExp(String.raw `^https?://([^./]+)\.(${common_1.wmf})\.org`, 'iu');
20
20
  /**
21
21
  * require一个JSON文件
22
22
  * @param file 文件名
package/dist/lib/lsp.js CHANGED
@@ -583,7 +583,7 @@ class LanguageService {
583
583
  this.config ??= index_1.default.getConfig();
584
584
  const { nsid, ext, html, parserFunction: [insensitive, sensitive, ...other], doubleUnderscore, protocol, img, } = this.config, tags = new Set([ext, html].flat(2));
585
585
  const re = new RegExp('(?:' // eslint-disable-line prefer-template
586
- + String.raw `<(\/?\w*)` // tag ($1)
586
+ + String.raw `<(/?\w*)` // tag ($1)
587
587
  + '|'
588
588
  + String.raw `(\{{2,4}|\[\[)\s*([^|{}<>[\]\s][^|{}<>[\]#]*)?` // braces and brackets ($2, $3)
589
589
  + '|'
@@ -625,7 +625,7 @@ class LanguageService {
625
625
  * @param position position / 位置
626
626
  */
627
627
  async provideCompletionItems(text, position) {
628
- const { re, allTags, functions, switches, jaSwitches, protocols, params, tags, ext, } = this.#prepareCompletionConfig(), { line, character } = position, curLine = text.split(/\r?\n/u, line + 1)[line], mt = re.exec(curLine?.slice(0, character) ?? '');
628
+ const { re, allTags, functions, switches, jaSwitches, protocols, params, tags, ext, } = this.#prepareCompletionConfig(), { line, character } = position, curLine = text.split(/\r?\n/u, line + 1)[line], mt = re.exec(curLine?.slice(Math.max(0, character - 1e4), character) ?? '');
629
629
  if (mt?.[1] !== undefined) { // tag
630
630
  const closing = mt[1].startsWith('/');
631
631
  return getCompletion(allTags, 'Class', mt[1].slice(closing ? 1 : 0), position, closing && !curLine?.slice(character).trimStart().startsWith('>') ? '>' : '');
package/dist/lib/text.js CHANGED
@@ -43,16 +43,6 @@ const errorSyntaxUrl = /* #__PURE__ */ new RegExp(source, 'giu'), noLinkTypes =
43
43
  'select',
44
44
  'textarea',
45
45
  ]);
46
- const wordRegex = /* #__PURE__ */ (() => {
47
- try {
48
- // eslint-disable-next-line prefer-regex-literals
49
- return new RegExp(String.raw `[\p{L}\p{N}_]`, 'u');
50
- }
51
- catch /* c8 ignore start */ {
52
- return /\w/u;
53
- }
54
- /* c8 ignore stop */
55
- })();
56
46
  /**
57
47
  * text node
58
48
  *
@@ -274,7 +264,7 @@ class AstText extends node_1.AstNode {
274
264
  if (char === '<') {
275
265
  e.suggestions = [(0, lint_1.fixByEscape)(startIndex, '&lt;')];
276
266
  }
277
- else if (char === 'h' && type !== 'link-text' && wordRegex.test(previousChar ?? '')) {
267
+ else if (char === 'h' && type !== 'link-text' && /[\p{L}\p{N}_]/u.test(previousChar ?? '')) {
278
268
  e.suggestions = [(0, lint_1.fixBySpace)(startIndex)];
279
269
  }
280
270
  else if (lbrackInExtLinkText) {
@@ -3,24 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseCommentAndExt = void 0;
4
4
  const common_1 = require("@bhsd/common");
5
5
  const string_1 = require("../util/string");
6
+ const replace_1 = require("../util/replace");
6
7
  const onlyinclude_1 = require("../src/onlyinclude");
7
8
  const noinclude_1 = require("../src/nowiki/noinclude");
8
9
  const include_1 = require("../src/tagPair/include");
9
10
  const ext_1 = require("../src/tagPair/ext");
10
11
  const comment_1 = require("../src/nowiki/comment");
11
- const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', { length } = onlyincludeLeft, getExtRegex = [false, true].map(includeOnly => {
12
+ const onlyincludeLeft = '<onlyinclude>', onlyincludeRight = '</onlyinclude>', getExtRegex = [false, true].map(includeOnly => {
12
13
  const noincludeRegex = includeOnly ? 'includeonly' : '(?:no|only)include', includeRegex = includeOnly ? 'noinclude' : 'includeonly';
13
- return (0, common_1.getRegex)(exts => new RegExp(String.raw `<!--.*?(?:-->|$)|<${noincludeRegex}(?:\s[^>]*)?/?>|</${noincludeRegex}\s*>|<(${exts // eslint-disable-next-line unicorn/prefer-string-raw
14
- })(\s[^>]*?)?(?:/>|>(.*?)</(${'\\1'}\s*)>)|<(${includeRegex})(\s[^>]*?)?(?:/>|>(.*?)(?:</(${includeRegex}\s*)>|$))`, 'gisu'));
14
+ return (0, common_1.getRegex)(exts => new RegExp(String.raw `<!--.*?(?:-->|$)|<${noincludeRegex}(?=\s|\/?>)|</${noincludeRegex}\s*>|<(${exts})(?=\s|\/?>)|<(${includeRegex})(?=\s|\/?>)`, 'gisu'));
15
15
  });
16
- /**
17
- * 更新`<onlyinclude>`和`</onlyinclude>`的位置
18
- * @param wikitext
19
- */
20
- const update = (wikitext) => {
21
- const i = wikitext.indexOf(onlyincludeLeft);
22
- return { i, j: wikitext.indexOf(onlyincludeRight, i + length) };
23
- };
24
16
  /**
25
17
  * 解析HTML注释和扩展标签
26
18
  * @param wikitext
@@ -30,32 +22,20 @@ const update = (wikitext) => {
30
22
  */
31
23
  const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
32
24
  if (includeOnly) {
33
- let { i, j } = update(wikitext);
34
- if (i !== -1 && j !== -1) { // `<onlyinclude>`拥有最高优先级
35
- let str = '';
36
- /**
37
- * 忽略未被`<onlyinclude>`和`</onlyinclude>`包裹的内容
38
- * @param text 未被包裹的内容
39
- */
40
- const noinclude = (text) => {
41
- // @ts-expect-error abstract class
42
- new noinclude_1.NoincludeToken(text, config, accum);
43
- str += `\0${accum.length - 1}n\x7F`;
44
- };
45
- while (i !== -1 && j !== -1) {
25
+ const i = wikitext.indexOf(onlyincludeLeft);
26
+ if (i !== -1 && wikitext.includes(onlyincludeRight, i)) { // `<onlyinclude>`拥有最高优先级
27
+ return (0, replace_1.safeStringReplace)(wikitext, onlyincludeLeft, onlyincludeRight, false, inner => {
46
28
  const token = `\0${accum.length}g\x7F`;
47
- new onlyinclude_1.OnlyincludeToken(wikitext.slice(i + length, j), config, accum);
48
- if (i > 0) {
49
- noinclude(wikitext.slice(0, i));
29
+ new onlyinclude_1.OnlyincludeToken(inner, config, accum);
30
+ return token;
31
+ }, s => {
32
+ if (!s) {
33
+ return '';
50
34
  }
51
- str += token;
52
- wikitext = wikitext.slice(j + length + 1);
53
- ({ i, j } = update(wikitext));
54
- }
55
- if (wikitext) {
56
- noinclude(wikitext);
57
- }
58
- return str;
35
+ // @ts-expect-error abstract class
36
+ new noinclude_1.NoincludeToken(s, config, accum);
37
+ return `\0${accum.length - 1}n\x7F`;
38
+ });
59
39
  }
60
40
  }
61
41
  const { ext } = config;
@@ -65,45 +45,103 @@ const parseCommentAndExt = (wikitext, config, accum, includeOnly) => {
65
45
  const stack = [];
66
46
  newExt = ext.filter(e => e !== 'translate' && e !== 'tvar');
67
47
  newConfig = { ...config, ext: newExt };
68
- wikitext = wikitext.replace(/<nowiki>.*?<\/nowiki>/gisu, m => {
69
- stack.push(m);
48
+ // 隐藏`<nowiki>`标签
49
+ const text = (0, replace_1.safeStringReplace)(wikitext, '<nowiki>', '</nowiki>', true, (inner, open, close) => {
50
+ stack.push(open + inner + close);
70
51
  return `\0${stack.length - 1}\x7F`;
71
- }).replace(/<translate( nowrap)?>(.*?)<\/translate>/gsu, (_, p1, p2) => {
52
+ });
53
+ // 解析`<translate>`标签
54
+ wikitext = (0, replace_1.safePatternReplace)(text, /<translate( nowrap)?>/gu, '</translate>', (inner, [, p1]) => {
72
55
  const l = accum.length;
73
56
  // @ts-expect-error abstract class
74
- new TranslateToken(p1, p2 && (0, string_1.restore)(p2, stack), newConfig, accum);
57
+ new TranslateToken(p1, inner && (0, string_1.restore)(inner, stack), newConfig, accum);
75
58
  return `\0${l}g\x7F`;
76
59
  });
77
60
  wikitext = (0, string_1.restore)(wikitext, stack);
78
61
  }
79
- const re = getExtRegex[includeOnly ? 1 : 0](newExt.join('|'));
62
+ const re = getExtRegex[includeOnly ? 1 : 0](newExt.join('|')), noMoreTags = new Set();
80
63
  re.lastIndex = 0;
81
- return re.test(wikitext)
82
- ? wikitext.replace(re, (substr, name, attr, inner, closing, include, includeAttr, includeInner, includeClosing) => {
83
- const l = accum.length;
84
- let ch = 'n';
85
- if (name) {
86
- ch = 'e';
87
- // @ts-expect-error abstract class
88
- new ext_1.ExtToken(name, attr, inner, closing, newConfig, include, accum);
64
+ let lastIndex = 0, text = '', noMoreGt = false;
65
+ for (let mt = re.exec(wikitext); mt; mt = re.exec(wikitext)) {
66
+ const [substr, name, include] = mt, tag = name || include, { index } = mt, l = accum.length;
67
+ text += wikitext.slice(lastIndex, index);
68
+ ({ lastIndex } = re);
69
+ let ch = 'n', matched = true;
70
+ if (substr.startsWith('<!--')) {
71
+ ch = 'c';
72
+ const closed = substr.endsWith('-->');
73
+ // @ts-expect-error abstract class
74
+ new comment_1.CommentToken((0, string_1.restore)(substr, accum, 1)
75
+ .slice(4, closed ? -3 : undefined), closed, config, accum);
76
+ }
77
+ else if (noMoreGt) {
78
+ matched = false;
79
+ }
80
+ else if (tag) {
81
+ const i = wikitext.indexOf('>', lastIndex);
82
+ let attr, inner, closing;
83
+ if (i === -1) {
84
+ matched = false;
85
+ noMoreGt = true;
89
86
  }
90
- else if (substr.startsWith('<!--')) {
91
- ch = 'c';
92
- const closed = substr.endsWith('-->');
93
- // @ts-expect-error abstract class
94
- new comment_1.CommentToken((0, string_1.restore)(substr, accum, 1)
95
- .slice(4, closed ? -3 : undefined), closed, config, accum);
87
+ else if (wikitext[i - 1] === '/') {
88
+ attr = wikitext.slice(lastIndex, i - 1);
89
+ lastIndex = i + 1;
90
+ re.lastIndex = lastIndex;
96
91
  }
97
- else if (include) {
98
- // @ts-expect-error abstract class
99
- new include_1.IncludeToken(include, includeAttr && (0, string_1.restore)(includeAttr, accum, 1), includeInner && (0, string_1.restore)(includeInner, accum, 1), includeClosing, config, accum);
92
+ else if (noMoreTags.has(tag.toLowerCase())) {
93
+ matched = false;
100
94
  }
101
95
  else {
96
+ attr = wikitext.slice(lastIndex, i);
97
+ const after = wikitext.slice(i + 1), m = new RegExp(String.raw `</(${tag}\s*)>`, 'iu').exec(after);
98
+ if (m) {
99
+ inner = after.slice(0, m.index);
100
+ [, closing] = m;
101
+ lastIndex = i + 1 + m.index + m[0].length;
102
+ re.lastIndex = lastIndex;
103
+ }
104
+ else if (include) {
105
+ inner = after;
106
+ lastIndex = wikitext.length;
107
+ re.lastIndex = lastIndex;
108
+ }
109
+ else {
110
+ matched = false;
111
+ noMoreTags.add(tag.toLowerCase());
112
+ }
113
+ }
114
+ if (matched) {
115
+ if (name) {
116
+ ch = 'e';
117
+ // @ts-expect-error abstract class
118
+ new ext_1.ExtToken(name, attr, inner, closing, newConfig, include, accum);
119
+ }
120
+ else {
121
+ // @ts-expect-error abstract class
122
+ new include_1.IncludeToken(include, attr && (0, string_1.restore)(attr, accum, 1), inner && (0, string_1.restore)(inner, accum, 1), closing, config, accum);
123
+ }
124
+ }
125
+ }
126
+ else if (substr.startsWith('</')) {
127
+ // @ts-expect-error abstract class
128
+ new noinclude_1.NoincludeToken(wikitext.slice(index, lastIndex), config, accum, true);
129
+ }
130
+ else {
131
+ const i = wikitext.indexOf('>', lastIndex);
132
+ if (i === -1) {
133
+ matched = false;
134
+ noMoreGt = true;
135
+ }
136
+ else {
137
+ lastIndex = i + 1;
138
+ re.lastIndex = lastIndex;
102
139
  // @ts-expect-error abstract class
103
- new noinclude_1.NoincludeToken(substr, config, accum, true);
140
+ new noinclude_1.NoincludeToken(wikitext.slice(index, lastIndex), config, accum, true);
104
141
  }
105
- return `\0${l}${ch}\x7F`;
106
- })
107
- : wikitext;
142
+ }
143
+ text += matched ? `\0${l}${ch}\x7F` : substr;
144
+ }
145
+ return text + wikitext.slice(lastIndex);
108
146
  };
109
147
  exports.parseCommentAndExt = parseCommentAndExt;
@@ -11,8 +11,8 @@ const converter_1 = require("../src/converter");
11
11
  const parseConverter = (text, config, accum) => {
12
12
  config.regexConverter ??= new RegExp(String.raw `;(?=(?:[^;]*?=>)?\s*(?:${config.variants.join('|')})\s*:|(?:\s|\0\d+[cn]\x7F)*$)`, 'u');
13
13
  const regex1 = /-\{/gu, regex2 = /-\{|\}-/gu, stack = [];
14
- let regex = regex1, mt = regex.exec(text);
15
- while (mt) {
14
+ let regex = regex1;
15
+ for (let mt = regex.exec(text); mt; mt = regex.exec(text)) {
16
16
  const { 0: syntax, index } = mt;
17
17
  if (syntax === '}-') {
18
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)
@@ -30,7 +30,6 @@ const parseConverter = (text, config, accum) => {
30
30
  regex = regex2;
31
31
  regex.lastIndex = index + 2;
32
32
  }
33
- mt = regex.exec(text);
34
33
  }
35
34
  return text;
36
35
  };
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseExternalLinks = void 0;
4
4
  const string_1 = require("../util/string");
5
+ const replace_1 = require("../util/replace");
5
6
  const extLink_1 = require("../src/extLink");
6
7
  const magicLink_1 = require("../src/magicLink");
7
8
  /**
@@ -12,13 +13,17 @@ const magicLink_1 = require("../src/magicLink");
12
13
  * @param inFile 是否在图链中
13
14
  */
14
15
  const parseExternalLinks = (wikitext, config, accum, inFile) => {
15
- config.regexExternalLinks ??= new RegExp(String.raw `\[((?:\0\d+[cn]\x7F)*(?:\0\d+f\x7F|(?:(?:${config.protocol}|//)${string_1.extUrlCharFirst}|\0\d+m\x7F)${string_1.extUrlChar}(?=[[\]<>"\t${string_1.zs}]|\0\d)))([${string_1.zs}]*(?![${string_1.zs}]))([^\]\x01-\x08\x0A-\x1F\uFFFD]*)\]`, 'giu');
16
- return wikitext.replace(config.regexExternalLinks, (_, url, space, text) => {
17
- const { length } = accum, mt = /&[lg]t;/u.exec(url);
18
- if (mt) {
16
+ config.regexExternalLinks ??= new RegExp(String.raw `\[((?:\0\d+[cn]\x7F)*(?:\0\d+f\x7F|(?:(?:${config.protocol}|//)${string_1.extUrlCharFirst}|\0\d+m\x7F)${string_1.extUrlChar}(?=[[\]<>"\t${string_1.zs}]|\0\d)))([${string_1.zs}]*(?![${string_1.zs}]))`, 'giu');
17
+ const re = config.regexExternalLinks;
18
+ return (0, replace_1.safePatternReplace)(wikitext, re, ']', (text, [, url, space]) => {
19
+ if (/[\x01-\x08\x0A-\x1F\uFFFD]/u.test(text)) {
20
+ return false;
21
+ }
22
+ const { length } = accum, i = url.search(/&[lg]t;/u);
23
+ if (i !== -1) {
19
24
  space = '';
20
- text = url.slice(mt.index) + space + text;
21
- url = url.slice(0, mt.index);
25
+ text = url.slice(i) + space + text;
26
+ url = url.slice(0, i);
22
27
  }
23
28
  if (inFile) {
24
29
  // @ts-expect-error abstract class
@@ -11,23 +11,15 @@ const space = String.raw `[${string_1.zs}\t]|&nbsp;|&#0*160;|&#x0*a0;`, sp = `(?
11
11
  * @param accum
12
12
  */
13
13
  const parseMagicLinks = (wikitext, config, accum) => {
14
- if (!config.regexMagicLinks) {
15
- try {
16
- config.regexMagicLinks = new RegExp(String.raw `(^|[^\p{L}\p{N}_])(?:(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})|${magicLinkPattern})`, 'giu');
17
- }
18
- catch /* c8 ignore start */ {
19
- config.regexMagicLinks = new RegExp(String.raw `(^|\W)(?:(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})|${magicLinkPattern})`, 'giu');
20
- }
21
- /* c8 ignore stop */
22
- }
14
+ config.regexMagicLinks ??= new RegExp(String.raw `(^|[^\p{L}\p{N}_])(?:(?:${config.protocol})(${string_1.extUrlCharFirst}${string_1.extUrlChar})|${magicLinkPattern})`, 'giu');
23
15
  return wikitext.replace(config.regexMagicLinks, (m, lead, p1) => {
24
16
  let url = lead ? m.slice(lead.length) : m;
25
17
  if (p1) {
26
18
  let trail = '';
27
- const m2 = /&(?:[lg]t|nbsp|#x0*(?:3[ce]|a0)|#0*(?:6[02]|160));/iu.exec(url);
28
- if (m2) {
29
- trail = url.slice(m2.index);
30
- url = url.slice(0, m2.index);
19
+ const index = url.search(/&(?:[lg]t|nbsp|#x0*(?:3[ce]|a0)|#0*(?:6[02]|160));/iu);
20
+ if (index !== -1) {
21
+ trail = url.slice(index);
22
+ url = url.slice(0, index);
31
23
  }
32
24
  const sep = url.includes('(') ? /[^,;\\.:!?][,;\\.:!?]+$/u : /[^,;\\.:!?)][,;\\.:!?)]+$/u, sepChars = sep.exec(url);
33
25
  if (sepChars) {
@@ -99,7 +99,7 @@ const parseTable = ({ firstChild: { data }, type, name }, config, accum) => {
99
99
  else {
100
100
  top = pop(top, stack);
101
101
  const regex = cell === '!' ? /!!|(?:\||\0\d+!\x7F){2}|\0\d+\+\x7F/gu : /(?:\||\0\d+!\x7F){2}|\0\d+\+\x7F/gu;
102
- let mt = regex.exec(attr), lastIndex = 0, lastSyntax = `\n${spaces}${cell}`;
102
+ let lastIndex = 0, lastSyntax = `\n${spaces}${cell}`, mt;
103
103
  /**
104
104
  * 创建表格单元格
105
105
  * @param tr 当前表格行
@@ -110,11 +110,10 @@ const parseTable = ({ firstChild: { data }, type, name }, config, accum) => {
110
110
  tr.insertAt(td);
111
111
  return td;
112
112
  };
113
- while (mt) {
113
+ for (mt = regex.exec(attr); mt; mt = regex.exec(attr)) {
114
114
  newTd(top);
115
115
  ({ lastIndex } = regex);
116
116
  [lastSyntax] = mt;
117
- mt = regex.exec(attr);
118
117
  }
119
118
  stack.push(top, newTd(top));
120
119
  }
@@ -14,7 +14,7 @@ const index_2 = require("./index");
14
14
  const atom_1 = require("./atom");
15
15
  /* NOT FOR BROWSER ONLY */
16
16
  const document_1 = require("../lib/document");
17
- const insecureStyle = /expression|(?:accelerator|-o-link(?:-source)?|-o-replace)\s*:|(?:url|src|image(?:-set)?)\s*\(|attr\s*\([^)]+[\s,]url/u, evil = /(?:^|\s|\*\/)(?:java|vb)script(?:\W|$)/iu, complexTypes = new Set(['ext', 'arg', 'magic-word', 'template']), itemAttrs = new Set(['itemtype', 'itemid', 'itemref']), urlAttrs = new Set([
17
+ const insecureStyle = /expression|(?:accelerator|-o-link(?:-source)?|-o-replace)\s*:|(?:url|src|image(?:-set)?)\s*\(|attr\s*\((?:(?!attr\s*\()[^)])+[\s,]url/u, evil = /(?:^|\s|\*\/)(?:java|vb)script(?:\W|$)/iu, complexTypes = new Set(['ext', 'arg', 'magic-word', 'template']), itemAttrs = new Set(['itemtype', 'itemid', 'itemref']), urlAttrs = new Set([
18
18
  'about',
19
19
  'property',
20
20
  'resource',
@@ -22,16 +22,6 @@ exports.toAttributeType = toAttributeType;
22
22
  * @param type 属性类型
23
23
  */
24
24
  const toDirty = (type) => `${(0, exports.toAttributeType)(type)}-dirty`;
25
- const wordRegex = /* #__PURE__ */ (() => {
26
- try {
27
- // eslint-disable-next-line prefer-regex-literals
28
- return new RegExp(String.raw `[\p{L}\p{N}]`, 'u');
29
- }
30
- catch /* c8 ignore start */ {
31
- return /[^\W_]/u;
32
- }
33
- /* c8 ignore stop */
34
- })();
35
25
  const required = new Map([
36
26
  ['indicator', ['name']],
37
27
  ['langconvert', ['from', 'to']],
@@ -63,14 +53,14 @@ class AttributesToken extends index_2.Token {
63
53
  this.setAttribute('name', name);
64
54
  if (attr) {
65
55
  const regex = /([^\s/](?:(?!\0\d+~\x7F)[^\s/=])*)(?:((?:\s(?:\s|\0\d+[cn]\x7F)*)?(?:=|\0\d+~\x7F)(?:\s|\0\d+[cn]\x7F)*)(?:(["'])(.*?)(\3|$)|(\S*)))?/gsu;
66
- let out = '', mt = regex.exec(attr), lastIndex = 0;
56
+ let out = '', lastIndex = 0;
67
57
  const insertDirty = /** 插入无效属性 */ () => {
68
58
  if (out) {
69
59
  super.insertAt(new atom_1.AtomToken(out, toDirty(type), config, accum));
70
60
  out = '';
71
61
  }
72
62
  };
73
- while (mt) {
63
+ for (let mt = regex.exec(attr); mt; mt = regex.exec(attr)) {
74
64
  const { index, 0: full, 1: key, 2: equal, 3: quoteStart, 4: quoted, 5: quoteEnd, 6: unquoted } = mt;
75
65
  out += attr.slice(lastIndex, index);
76
66
  if (/^(?:[\w:]|\0\d+t\x7F)(?:[\w:.-]|\0\d+t\x7F)*$/u
@@ -85,7 +75,6 @@ class AttributesToken extends index_2.Token {
85
75
  out += full;
86
76
  }
87
77
  ({ lastIndex } = regex);
88
- mt = regex.exec(attr);
89
78
  }
90
79
  out += attr.slice(lastIndex);
91
80
  insertDirty();
@@ -185,7 +174,7 @@ class AttributesToken extends index_2.Token {
185
174
  }
186
175
  }
187
176
  else {
188
- const str = attr.text().trim(), severity = s[wordRegex.test(str) ? 1 : 2];
177
+ const str = attr.text().trim(), severity = s[/[\p{L}\p{N}]/u.test(str) ? 1 : 2];
189
178
  if (str && severity) {
190
179
  const e = (0, lint_1.generateForChild)(attr, rect, rules[0], 'invalid-attribute', severity);
191
180
  if (computeEditInfo) {
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CommentedToken = void 0;
4
+ const replace_1 = require("../util/replace");
4
5
  const index_1 = require("./index");
5
6
  const comment_1 = require("./nowiki/comment");
6
7
  /**
@@ -15,25 +16,19 @@ class CommentedToken extends index_1.Token {
15
16
  constructor(wikitext, config, accum = []) {
16
17
  super(undefined, config, accum, {});
17
18
  if (wikitext) {
18
- let i = wikitext.indexOf('<!--'), j = i !== -1 && wikitext.indexOf('-->', i + 4), lastIndex = 0;
19
- while (j !== false && j !== -1) {
20
- if (i > lastIndex) {
21
- this.insertAt(wikitext.slice(lastIndex, i));
22
- }
19
+ (0, replace_1.safeStringReplace)(wikitext, '<!--', '-->', false, inner => {
23
20
  // @ts-expect-error abstract class
24
- this.insertAt(new comment_1.CommentToken(wikitext.slice(i + 4, j), true, config, accum));
25
- lastIndex = j + 3;
26
- i = wikitext.indexOf('<!--', lastIndex);
27
- j = i !== -1 && wikitext.indexOf('-->', i + 4);
28
- }
29
- if (lastIndex < wikitext.length) {
30
- this.insertAt(wikitext.slice(lastIndex));
31
- }
21
+ this.insertAt(new comment_1.CommentToken(inner, true, config, accum));
22
+ return '';
23
+ }, s => {
24
+ this.insertAt(s);
25
+ return '';
26
+ });
32
27
  }
33
28
  }
34
29
  /** @private */
35
30
  lint(start = this.getAbsoluteIndex()) {
36
- LINT: return super.lint(start, new RegExp(String.raw `<\s*(?:\/\s*)?(${this.name})\b`, 'giu'));
31
+ LINT: return super.lint(start, new RegExp(String.raw `<\s*(?:/\s*)?(${this.name})\b`, 'giu'));
37
32
  }
38
33
  }
39
34
  exports.CommentedToken = CommentedToken;
package/dist/src/index.js CHANGED
@@ -424,8 +424,7 @@ class Token extends element_1.AstElement {
424
424
  }
425
425
  if (!ignoreDisables) {
426
426
  const regex = new RegExp(String.raw `<!--\s*${configurationComment}-(disable(?:(?:-next)?-line)?|enable)(\s[\sa-z,-]*)?-->`, 'gu'), ignores = [];
427
- let mt = regex.exec(wikitext);
428
- while (mt) {
427
+ for (let mt = regex.exec(wikitext); mt; mt = regex.exec(wikitext)) {
429
428
  const { 1: type, index } = mt, detail = mt[2]?.trim();
430
429
  let line;
431
430
  if (type === 'disable-line' || type === 'disable-next-line') {
@@ -437,7 +436,6 @@ class Token extends element_1.AstElement {
437
436
  to: type === 'enable' ? regex.lastIndex : undefined,
438
437
  rules: detail ? new Set(detail.split(',').map(rule => rule.trim())) : undefined,
439
438
  });
440
- mt = regex.exec(wikitext);
441
439
  }
442
440
  errors = errors.filter(({ rule, startLine, startIndex }) => {
443
441
  const nearest = { pos: 0 };
@@ -25,8 +25,8 @@ const explode = (str) => {
25
25
  return [];
26
26
  }
27
27
  const regex = /-\{|\}-|\|/gu, exploded = [];
28
- let mt = regex.exec(str), depth = 0, lastIndex = 0;
29
- while (mt) {
28
+ let depth = 0, lastIndex = 0;
29
+ for (let mt = regex.exec(str); mt; mt = regex.exec(str)) {
30
30
  const { 0: match, index } = mt;
31
31
  if (match !== '|') {
32
32
  depth += match === '-{' ? 1 : -1;
@@ -35,7 +35,6 @@ const explode = (str) => {
35
35
  exploded.push(str.slice(lastIndex, index));
36
36
  ({ lastIndex } = regex);
37
37
  }
38
- mt = regex.exec(str);
39
38
  }
40
39
  exploded.push(str.slice(lastIndex));
41
40
  return exploded;
@@ -40,13 +40,51 @@ class NestedToken extends index_2.Token {
40
40
  wikitext &&= (0, braces_1.parseBraces)(wikitext, config, accum);
41
41
  accum.splice(length, 1);
42
42
  }
43
- else {
44
- wikitext &&= wikitext.replace(regex, (_, name, attr, inner, closing) => {
45
- const str = `\0${accum.length + 1}e\x7F`;
46
- // @ts-expect-error abstract class
47
- new ext_1.ExtToken(name, attr, inner, closing, config, false, accum);
48
- return str;
49
- });
43
+ else if (wikitext) {
44
+ const noMoreTags = new Set();
45
+ regex.lastIndex = 0;
46
+ let lastIndex = 0, text = '';
47
+ for (let mt = regex.exec(wikitext); mt; mt = regex.exec(wikitext)) {
48
+ const i = wikitext.indexOf('>', regex.lastIndex);
49
+ if (i === -1) {
50
+ break;
51
+ }
52
+ const [substr, name] = mt, { index } = mt;
53
+ text += wikitext.slice(lastIndex, index);
54
+ ({ lastIndex } = regex);
55
+ let matched = true, attr, inner, closing;
56
+ if (wikitext[i - 1] === '/') {
57
+ attr = wikitext.slice(lastIndex, i - 1);
58
+ lastIndex = i + 1;
59
+ regex.lastIndex = lastIndex;
60
+ }
61
+ else if (noMoreTags.has(name.toLowerCase())) {
62
+ matched = false;
63
+ }
64
+ else {
65
+ const after = wikitext.slice(i + 1), m = new RegExp(String.raw `</(${name}\s*)>`, 'iu').exec(after);
66
+ if (m) {
67
+ inner = after.slice(0, m.index);
68
+ [, closing] = m;
69
+ lastIndex = i + 1 + m.index + m[0].length;
70
+ regex.lastIndex = lastIndex;
71
+ }
72
+ else {
73
+ matched = false;
74
+ noMoreTags.add(name.toLowerCase());
75
+ }
76
+ }
77
+ if (matched) {
78
+ const str = `\0${accum.length + 1}e\x7F`;
79
+ // @ts-expect-error abstract class
80
+ new ext_1.ExtToken(name, attr, inner, closing, config, false, accum);
81
+ text += str;
82
+ }
83
+ else {
84
+ text += substr;
85
+ }
86
+ }
87
+ wikitext = text + wikitext.slice(lastIndex);
50
88
  }
51
89
  wikitext &&= wikitext.replace(/(^|\0\d+.\x7F)([^\0]+)(?=$|\0\d+.\x7F)/gu, (_, lead, substr) => {
52
90
  // @ts-expect-error abstract class
@@ -8,7 +8,7 @@ const string_1 = require("../util/string");
8
8
  const lint_1 = require("../util/lint");
9
9
  const index_1 = __importDefault(require("../index"));
10
10
  const index_2 = require("./index");
11
- const linkRegex = /* #__PURE__ */ (() => new RegExp(`https?://${string_1.extUrlCharFirst}${string_1.extUrlChar}$`, 'iu'))();
11
+ const linkRegex = /* #__PURE__ */ (() => new RegExp(`^https?://${string_1.extUrlCharFirst}${string_1.extUrlChar}$`, 'u'))();
12
12
  /**
13
13
  * template or magic word parameter
14
14
  *
@@ -74,9 +74,9 @@ class ParameterToken extends index_2.Token {
74
74
  LINT: {
75
75
  const errors = super.lint(start, re), rule = 'unescaped', { lintConfig } = index_1.default, s = lintConfig.getSeverity(rule);
76
76
  if (s) {
77
- const { firstChild } = this, link = linkRegex.exec(firstChild.text())?.[0];
77
+ const { firstChild } = this, key = firstChild.text().toLowerCase(), i = Math.max(key.lastIndexOf('http://'), key.lastIndexOf('https://')), link = i !== -1 && key.slice(i);
78
78
  try {
79
- if (link && new URL(link).search) {
79
+ if (link && linkRegex.test(link) && new URL(link).search) {
80
80
  const e = (0, lint_1.generateForChild)(firstChild, { start }, rule, 'unescaped-query', s);
81
81
  e.startIndex = e.endIndex;
82
82
  e.startLine = e.endLine;
package/dist/src/pre.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PreToken = void 0;
4
4
  const constants_1 = require("../util/constants");
5
+ const replace_1 = require("../util/replace");
5
6
  const index_1 = require("./index");
6
7
  const noinclude_1 = require("./nowiki/noinclude");
7
8
  /**
@@ -14,29 +15,13 @@ class PreToken extends index_1.Token {
14
15
  }
15
16
  /** @class */
16
17
  constructor(wikitext, config, accum = []) {
17
- if (wikitext) {
18
- const opening = /<nowiki>/giu, closing = /<\/nowiki>/giu, { length } = opening.source;
19
- let i = opening.exec(wikitext);
20
- if (i) {
21
- closing.lastIndex = i.index + length;
22
- }
23
- let j = closing.exec(wikitext), lastIndex = 0, str = '';
24
- while (i && j) {
25
- // @ts-expect-error abstract class
26
- new noinclude_1.NoincludeToken(i[0], config, accum, true);
27
- // @ts-expect-error abstract class
28
- new noinclude_1.NoincludeToken(j[0], config, accum, true);
29
- str += `${wikitext.slice(lastIndex, i.index)}\0${accum.length - 1}n\x7F${wikitext.slice(i.index + length, j.index)}\0${accum.length}n\x7F`;
30
- lastIndex = j.index + length + 1;
31
- opening.lastIndex = lastIndex;
32
- i = opening.exec(wikitext);
33
- if (i) {
34
- closing.lastIndex = i.index + length;
35
- }
36
- j = closing.exec(wikitext);
37
- }
38
- wikitext = str + wikitext.slice(lastIndex);
39
- }
18
+ wikitext &&= (0, replace_1.safeStringReplace)(wikitext, '<nowiki>', '</nowiki>', true, (inner, opening, closing) => {
19
+ // @ts-expect-error abstract class
20
+ new noinclude_1.NoincludeToken(opening, config, accum, true);
21
+ // @ts-expect-error abstract class
22
+ new noinclude_1.NoincludeToken(closing, config, accum, true);
23
+ return `\0${accum.length - 1}n\x7F${inner}\0${accum.length}n\x7F`;
24
+ });
40
25
  super(wikitext, config, accum, {});
41
26
  this.setAttribute('stage', constants_1.MAX_STAGE - 1);
42
27
  }
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.CharinsertLineToken = void 0;
7
7
  const lint_1 = require("../../util/lint");
8
+ const replace_1 = require("../../util/replace");
8
9
  const rect_1 = require("../../lib/rect");
9
10
  const index_1 = __importDefault(require("../../index"));
10
11
  const index_2 = require("./index");
@@ -21,9 +22,9 @@ class CharinsertLineToken extends index_2.SingleLineToken {
21
22
  }
22
23
  /** @class */
23
24
  constructor(wikitext, config, accum) {
24
- super(wikitext?.replace(/<(nowiki)>(.*?)<\/(nowiki)>/giu, (_, opening, content, closing) => {
25
+ super(wikitext && (0, replace_1.safeStringReplace)(wikitext, '<nowiki>', '</nowiki>', true, (content, opening, closing) => {
25
26
  // @ts-expect-error abstract class
26
- new ext_1.ExtToken(opening, undefined, content, closing, config, false, accum);
27
+ new ext_1.ExtToken(opening.slice(1, -1), undefined, content, closing.slice(2, -1), config, false, accum);
27
28
  return `\0${accum.length - 2}e\x7F`;
28
29
  }), config, accum);
29
30
  }
@@ -37,22 +38,21 @@ class CharinsertLineToken extends index_2.SingleLineToken {
37
38
  const errors = [], str = this.childNodes.map(child => child.type === 'text'
38
39
  ? child.data
39
40
  : `<nowiki>${child.innerText.replace(/[^+]/gu, '_')}</nowiki>`).join(''), re = /(?:^|\s)\S+/gu, rect = new rect_1.BoundingRect(this, start), msg = index_1.default.msg('invalid-content', 'charinsert');
40
- let mt = re.exec(str);
41
- while (mt) {
41
+ for (let mt = re.exec(str); mt; mt = re.exec(str)) {
42
42
  const [item] = mt;
43
43
  let i = item.indexOf('+');
44
44
  if (i !== -1 && item.slice(0, i).trim()) {
45
45
  i = item.indexOf('+', i + 1);
46
46
  }
47
- if (i !== -1) {
48
- const e = (0, lint_1.generateForSelf)(this, rect, rule, msg, s), startOffset = mt.index + i, endOffset = mt.index + item.length;
49
- e.endIndex = start + endOffset;
50
- e.endCol = e.startCol + endOffset;
51
- e.startIndex += startOffset;
52
- e.startCol += startOffset;
53
- errors.push(e);
47
+ if (i === -1) {
48
+ continue;
54
49
  }
55
- mt = re.exec(str);
50
+ const e = (0, lint_1.generateForSelf)(this, rect, rule, msg, s), startOffset = mt.index + i, endOffset = mt.index + item.length;
51
+ e.endIndex = start + endOffset;
52
+ e.endCol = e.startCol + endOffset;
53
+ e.startIndex += startOffset;
54
+ e.startCol += startOffset;
55
+ errors.push(e);
56
56
  }
57
57
  return errors;
58
58
  }
@@ -152,14 +152,14 @@ let ExtToken = (() => {
152
152
  // NestedToken 依赖 ExtToken
153
153
  const { NestedToken } = require('../nested');
154
154
  // @ts-expect-error abstract class
155
- innerToken = new NestedToken(inner, /<(option|choicetemplate)(\s[^>]*?)?(?:\/>|>(.*?)<\/(\1)>)/gsu, ['option', 'choicetemplate'], newConfig, accum);
155
+ innerToken = new NestedToken(inner, /<(option|choicetemplate)(?=\s|\/?>)/gu, ['option', 'choicetemplate'], newConfig, accum);
156
156
  break;
157
157
  }
158
158
  case 'combobox': {
159
159
  // NestedToken 依赖 ExtToken
160
160
  const { NestedToken } = require('../nested');
161
161
  // @ts-expect-error abstract class
162
- innerToken = new NestedToken(inner, /<(combooption)(\s[^>]*?)?(?:\/>|>(.*?)<\/(combooption\s*)>)/gisu, ['combooption'], newConfig, accum);
162
+ innerToken = new NestedToken(inner, /<(combooption)(?=\s|\/?>)/gu, ['combooption'], newConfig, accum);
163
163
  break;
164
164
  }
165
165
  case 'gallery': {
@@ -22,19 +22,49 @@ class TranslateToken extends index_2.TagPairToken {
22
22
  */
23
23
  constructor(attr, inner, config, accum = []) {
24
24
  const attrToken = new syntax_1.SyntaxToken(attr, 'translate-attr', config, accum);
25
- inner = inner?.replace(/<tvar(\|[^>]+)>(.*?)<\/>/gsu, (_, p1, p2) => {
26
- // @ts-expect-error abstract class
27
- new tvar_1.TvarToken('tvar', p1, false, config, accum);
28
- // @ts-expect-error abstract class
29
- new tvar_1.TvarToken('', '', true, config, accum);
30
- return `\0${accum.length - 2}n\x7F${p2}\0${accum.length}n\x7F`;
31
- }).replace(/<(tvar)(\s+name\s*=(?:\s*(?:(["']).*?\3|[^"'\s>]+))?\s*)>(.*?)<\/(tvar)(\s*)>/gisu, (_, p1, p2, __, p3, p4, p5) => {
32
- // @ts-expect-error abstract class
33
- new tvar_1.TvarToken(p1, p2, false, config, accum);
34
- // @ts-expect-error abstract class
35
- new tvar_1.TvarToken(p4, p5, true, config, accum);
36
- return `\0${accum.length - 2}n\x7F${p3}\0${accum.length}n\x7F`;
37
- });
25
+ if (inner) {
26
+ let result = '', lastIndex = 0, i, j, k, index;
27
+ const update = /** @ignore */ () => {
28
+ i = inner.indexOf('<tvar|', lastIndex);
29
+ index = i + 6;
30
+ j = inner.indexOf('>', index);
31
+ k = inner.indexOf('</>', j + 1);
32
+ return [i, j === index ? -1 : j, k];
33
+ };
34
+ for ([i, j, k] = update(); i !== -1 && j !== -1 && k !== -1; [i, j, k] = update()) {
35
+ result += inner.slice(lastIndex, i);
36
+ lastIndex = k + 3;
37
+ // @ts-expect-error abstract class
38
+ new tvar_1.TvarToken('tvar', inner.slice(index - 1, j), false, config, accum);
39
+ // @ts-expect-error abstract class
40
+ new tvar_1.TvarToken('', '', true, config, accum);
41
+ result += `\0${accum.length - 2}n\x7F${inner.slice(j + 1, k)}\0${accum.length}n\x7F`;
42
+ }
43
+ inner = result + inner.slice(lastIndex);
44
+ result = '';
45
+ lastIndex = 0;
46
+ const start = /<(tvar)(\s+name\s*=)/giu, end = /<\/(tvar)(\s*)>/giu;
47
+ for (let mt = start.exec(inner); mt; mt = start.exec(inner)) {
48
+ i = inner.indexOf('>', start.lastIndex);
49
+ const name = inner.slice(start.lastIndex, i);
50
+ if (i === -1 || !/^(?:\s*(?:(["']).*?\1|[^"'\s>]+))?\s*$/su.test(name)) {
51
+ break;
52
+ }
53
+ end.lastIndex = i + 1;
54
+ const mt2 = end.exec(inner);
55
+ if (!mt2) {
56
+ break;
57
+ }
58
+ // @ts-expect-error abstract class
59
+ new tvar_1.TvarToken(mt[1], mt[2] + name, false, config, accum);
60
+ // @ts-expect-error abstract class
61
+ new tvar_1.TvarToken(mt2[1], mt2[2], true, config, accum);
62
+ result += `${inner.slice(lastIndex, mt.index)}\0${accum.length - 2}n\x7F${inner.slice(i + 1, mt2.index)}\0${accum.length}n\x7F`;
63
+ ({ lastIndex } = end);
64
+ start.lastIndex = lastIndex;
65
+ }
66
+ inner = result + inner.slice(lastIndex);
67
+ }
38
68
  const innerToken = new index_1.Token(inner, config, accum);
39
69
  innerToken.type = 'translate-inner';
40
70
  super('translate', attrToken, innerToken, 'translate', config, accum);
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.safePatternReplace = exports.safeStringReplace = void 0;
4
+ /**
5
+ * 使用`String#indexOf`避免正则表达式的回溯
6
+ * @param wikitext 源文本
7
+ * @param start 起始字符串
8
+ * @param end 结束字符串
9
+ * @param insensitive 是否忽略大小写
10
+ * @param matched 匹配到的内容处理函数
11
+ * @param unmatched 未匹配到的内容处理函数
12
+ */
13
+ const safeStringReplace = (wikitext, start, end, insensitive, matched, unmatched = (str) => str) => {
14
+ const text = insensitive ? wikitext.toLowerCase() : wikitext, lStart = start.length, lEnd = end.length;
15
+ let result = '', lastIndex = 0, i, j, index;
16
+ const update = /** @ignore */ () => {
17
+ i = text.indexOf(start, lastIndex);
18
+ index = i + lStart;
19
+ return [i, text.indexOf(end, index), index];
20
+ };
21
+ for ([i, j, index] = update(); i !== -1 && j !== -1; [i, j, index] = update()) {
22
+ result += unmatched(wikitext.slice(lastIndex, i));
23
+ lastIndex = j + lEnd;
24
+ result += matched(wikitext.slice(index, j), insensitive ? wikitext.slice(i, index) : start, insensitive ? wikitext.slice(j, lastIndex) : end);
25
+ }
26
+ return result + unmatched(wikitext.slice(lastIndex));
27
+ };
28
+ exports.safeStringReplace = safeStringReplace;
29
+ /**
30
+ * 使用`RegExp#exec`避免正则表达式的回溯
31
+ * @param wikitext 源文本
32
+ * @param start 起始正则表达式
33
+ * @param end 结束字符串
34
+ * @param matched 匹配到的内容处理函数
35
+ */
36
+ const safePatternReplace = (wikitext, start, end, matched) => {
37
+ let result = '', lastIndex = 0;
38
+ start.lastIndex = 0;
39
+ for (let mt = start.exec(wikitext); mt; mt = start.exec(wikitext)) {
40
+ const i = wikitext.indexOf(end, start.lastIndex);
41
+ if (i === -1) {
42
+ break;
43
+ }
44
+ const { index } = mt;
45
+ result += wikitext.slice(lastIndex, index);
46
+ lastIndex = i + end.length;
47
+ const r = matched(wikitext.slice(start.lastIndex, i), mt);
48
+ if (r === false) {
49
+ ({ lastIndex } = start);
50
+ result += wikitext.slice(index, lastIndex);
51
+ }
52
+ else {
53
+ result += r;
54
+ start.lastIndex = lastIndex;
55
+ }
56
+ }
57
+ return result + wikitext.slice(lastIndex);
58
+ };
59
+ exports.safePatternReplace = safePatternReplace;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikilint",
3
- "version": "2.48.1",
3
+ "version": "2.48.2",
4
4
  "description": "A Node.js linter for MediaWiki markup",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -75,7 +75,7 @@
75
75
  },
76
76
  "peerDependencies": {
77
77
  "mathoid-texvcjs": ">=0.4.0",
78
- "stylelint": ">=16.0.0",
78
+ "stylelint-config-recommended": ">=14.0.0",
79
79
  "vscode-css-languageservice": ">=3.0.0",
80
80
  "vscode-html-languageservice": ">=3.1.0",
81
81
  "vscode-json-languageservice": ">=3.2.0"
@@ -84,7 +84,7 @@
84
84
  "mathoid-texvcjs": {
85
85
  "optional": true
86
86
  },
87
- "stylelint": {
87
+ "stylelint-config-recommended": {
88
88
  "optional": true
89
89
  },
90
90
  "vscode-css-languageservice": {
@@ -100,13 +100,13 @@
100
100
  "dependencies": {
101
101
  "@bhsd/cm-util": "^2.4.2",
102
102
  "@bhsd/common": "^4.1.0",
103
- "@bhsd/stylelint-util": "^1.1.0",
103
+ "@bhsd/stylelint-util": "^2.0.0",
104
104
  "culori": "^4.0.2",
105
105
  "entities": "^8.1.0",
106
- "vscode-languageserver-types": "^3.18.4"
106
+ "vscode-languageserver-types": "^3.18.5"
107
107
  },
108
108
  "devDependencies": {
109
- "@bhsd/code-standard": "^5.0.0",
109
+ "@bhsd/code-standard": "^5.1.0",
110
110
  "@bhsd/nodejs": "^1.2.1",
111
111
  "@bhsd/test-util": "^3.3.0",
112
112
  "ajv": "^8.20.0",
@@ -114,14 +114,15 @@
114
114
  "c8": "^12.0.0",
115
115
  "diff2html-cli": "^5.2.15",
116
116
  "esbuild": "^0.28.2",
117
- "eslint": "^10.13.0",
117
+ "eslint": "^10.14.0",
118
118
  "mathoid-texvcjs": "^0.6.0",
119
119
  "stylelint": "^17.16.0",
120
+ "stylelint-config-recommended": "^18.0.0",
120
121
  "typescript": "^6.0.3",
121
122
  "vscode-css-languageservice": "^6.3.10",
122
123
  "vscode-html-languageservice": "^5.6.2",
123
124
  "vscode-json-languageservice": "^5.7.2",
124
- "vscode-languageserver-textdocument": "^1.0.15"
125
+ "vscode-languageserver-textdocument": "^1.0.16"
125
126
  },
126
127
  "engines": {
127
128
  "node": "^20.19.0 || ^22.13.0 || >=24.11.0"