wikilint 2.47.0 → 2.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.47.0']
100
+ additional_dependencies: ['wikilint@2.48.0']
101
101
  files: \.(mediawiki|wiki|wikitext)$
102
102
  ```
@@ -1023,6 +1023,7 @@
1023
1023
  "DEFAULTSORT": "defaultsort",
1024
1024
  "DEFAULTSORTKEY": "defaultsort",
1025
1025
  "DEFAULTCATEGORYSORT": "defaultsort",
1026
+ "CATEGORYSORT": "categorysort",
1026
1027
  "分类中页面数": "pagesincategory",
1027
1028
  "カテゴリ内ページ数": "pagesincategory",
1028
1029
  "PAGESINCATEGORY": "pagesincategory",
@@ -456,6 +456,7 @@
456
456
  "DEFAULTSORT": "defaultsort",
457
457
  "DEFAULTSORTKEY": "defaultsort",
458
458
  "DEFAULTCATEGORYSORT": "defaultsort",
459
+ "CATEGORYSORT": "categorysort",
459
460
  "PAGESINCATEGORY": "pagesincategory",
460
461
  "PAGESINCAT": "pagesincategory",
461
462
  "PAGESIZE": "pagesize",
@@ -546,6 +546,7 @@
546
546
  "DEFAULTSORT": "defaultsort",
547
547
  "DEFAULTSORTKEY": "defaultsort",
548
548
  "DEFAULTCATEGORYSORT": "defaultsort",
549
+ "CATEGORYSORT": "categorysort",
549
550
  "カテゴリ内ページ数": "pagesincategory",
550
551
  "PAGESINCATEGORY": "pagesincategory",
551
552
  "PAGESINCAT": "pagesincategory",
@@ -682,6 +682,7 @@
682
682
  "默认排序": "defaultsort",
683
683
  "默认排序关键字": "defaultsort",
684
684
  "默认分类排序": "defaultsort",
685
+ "CATEGORYSORT": "categorysort",
685
686
  "PAGESINCATEGORY": "pagesincategory",
686
687
  "PAGESINCAT": "pagesincategory",
687
688
  "分类中页面数": "pagesincategory",
@@ -2158,6 +2158,26 @@
2158
2158
  ],
2159
2159
  "description": "Used for categorizing pages, sets a default [category sort key](https://www.mediawiki.org/wiki/Help:Categories). It can take a second argument of *noerror* or *noreplace* to suppress error messages when multiple defaultsortkeys are used on one page or to make this `defaultsort` do nothing if a `defaultsort` was already specified earlier in the page."
2160
2160
  },
2161
+ {
2162
+ "aliases": [
2163
+ "categorysort"
2164
+ ],
2165
+ "signatures": [
2166
+ [
2167
+ {
2168
+ "label": "TIMESTAMP",
2169
+ "const": true
2170
+ }
2171
+ ],
2172
+ [
2173
+ {
2174
+ "label": "RTIMESTAMP",
2175
+ "const": true
2176
+ }
2177
+ ]
2178
+ ],
2179
+ "description": "When placed on a category page, the category will be sorted based on timestamp of categorization. `{{CATEGORYSORT:TIMESTAMP}}` lists members by the time they were added to the category, oldest first, and `{{CATEGORYSORT:RTIMESTAMP}}` lists the most recently added members first. Readers can still override this with the `cldsort` URL parameter, and the new `cldorder=desc` URL parameter reverses a `cldsort=timestamp` listing."
2180
+ },
2161
2181
  {
2162
2182
  "aliases": [
2163
2183
  "numberofpages"
package/dist/base.d.mts CHANGED
@@ -117,6 +117,7 @@ export type CompletionItem = Omit<CompletionItemBase, 'kind'> & {
117
117
  kind?: CompletionItemKind | keyof typeof CompletionItemKind;
118
118
  };
119
119
  export interface LanguageService {
120
+ /** @since v1.17.1 */
120
121
  include: boolean;
121
122
  /**
122
123
  * Destroy the instance
@@ -147,6 +148,14 @@ export interface LanguageService {
147
148
  * @param position position / 位置
148
149
  */
149
150
  provideCompletionItems(text: string, position: Position): Promise<CompletionItem[] | undefined>;
151
+ /**
152
+ * Add additional information to a completion item
153
+ *
154
+ * 为自动补全项添加额外信息
155
+ * @param item completion item / 自动补全项
156
+ * @since v1.48.0
157
+ */
158
+ resolveCompletionItem(item: CompletionItem): CompletionItem;
150
159
  /**
151
160
  * Provide grammar check
152
161
  *
@@ -160,6 +169,7 @@ export interface LanguageService {
160
169
  *
161
170
  * 实现修复全部代码的操作
162
171
  * @param action code action / 代码操作
172
+ * @since v1.24.1
163
173
  */
164
174
  resolveCodeAction(action: CodeAction): CodeAction;
165
175
  /**
@@ -182,6 +192,7 @@ export interface LanguageService {
182
192
  * 提供文档高亮
183
193
  * @param text source Wikitext / 源代码
184
194
  * @param position position / 位置
195
+ * @since v1.46.1
185
196
  */
186
197
  provideDocumentHighlights(text: string, position: Position): Promise<DocumentHighlight[] | undefined>;
187
198
  /**
@@ -238,6 +249,7 @@ export interface LanguageService {
238
249
  *
239
250
  * 提供 CodeLens
240
251
  * @param text source Wikitext / 源代码
252
+ * @since v1.16.3
241
253
  */
242
254
  provideInlayHints(text: string): Promise<InlayHint[]>;
243
255
  /**
@@ -246,6 +258,7 @@ export interface LanguageService {
246
258
  * 提供重构操作
247
259
  * @param text source Wikitext / 源代码
248
260
  * @param range range of the refactoring / 重构范围
261
+ * @since v1.24.1
249
262
  */
250
263
  provideRefactoringAction(text: string, range?: Range): Promise<CodeAction[]>;
251
264
  /**
@@ -268,6 +281,7 @@ export interface LanguageService {
268
281
  * 设置目标维基百科
269
282
  * @param wiki Wikipedia URL / 维基百科网址
270
283
  * @param user URI for wiki userpage or email address of the user / 维基用户页面地址或用户的电子邮件地址
284
+ * @since v1.18.1
271
285
  */
272
286
  setTargetWikipedia(wiki: string, user: string): Promise<void>;
273
287
  [Symbol.dispose](): void;
@@ -302,6 +316,7 @@ export interface Parser {
302
316
  *
303
317
  * 获取当前的解析设置
304
318
  * @param config unprocessed parser configuration / 未处理的解析设置
319
+ * @throws `RangeError` if the configuration file path or file name is invalid
305
320
  */
306
321
  getConfig(config?: ConfigData): Config;
307
322
  /**
package/dist/base.d.ts CHANGED
@@ -117,6 +117,7 @@ export type CompletionItem = Omit<CompletionItemBase, 'kind'> & {
117
117
  kind?: CompletionItemKind | keyof typeof CompletionItemKind;
118
118
  };
119
119
  export interface LanguageService {
120
+ /** @since v1.17.1 */
120
121
  include: boolean;
121
122
  /**
122
123
  * Destroy the instance
@@ -147,6 +148,14 @@ export interface LanguageService {
147
148
  * @param position position / 位置
148
149
  */
149
150
  provideCompletionItems(text: string, position: Position): Promise<CompletionItem[] | undefined>;
151
+ /**
152
+ * Add additional information to a completion item
153
+ *
154
+ * 为自动补全项添加额外信息
155
+ * @param item completion item / 自动补全项
156
+ * @since v1.48.0
157
+ */
158
+ resolveCompletionItem(item: CompletionItem): CompletionItem;
150
159
  /**
151
160
  * Provide grammar check
152
161
  *
@@ -160,6 +169,7 @@ export interface LanguageService {
160
169
  *
161
170
  * 实现修复全部代码的操作
162
171
  * @param action code action / 代码操作
172
+ * @since v1.24.1
163
173
  */
164
174
  resolveCodeAction(action: CodeAction): CodeAction;
165
175
  /**
@@ -182,6 +192,7 @@ export interface LanguageService {
182
192
  * 提供文档高亮
183
193
  * @param text source Wikitext / 源代码
184
194
  * @param position position / 位置
195
+ * @since v1.46.1
185
196
  */
186
197
  provideDocumentHighlights(text: string, position: Position): Promise<DocumentHighlight[] | undefined>;
187
198
  /**
@@ -238,6 +249,7 @@ export interface LanguageService {
238
249
  *
239
250
  * 提供 CodeLens
240
251
  * @param text source Wikitext / 源代码
252
+ * @since v1.16.3
241
253
  */
242
254
  provideInlayHints(text: string): Promise<InlayHint[]>;
243
255
  /**
@@ -246,6 +258,7 @@ export interface LanguageService {
246
258
  * 提供重构操作
247
259
  * @param text source Wikitext / 源代码
248
260
  * @param range range of the refactoring / 重构范围
261
+ * @since v1.24.1
249
262
  */
250
263
  provideRefactoringAction(text: string, range?: Range): Promise<CodeAction[]>;
251
264
  /**
@@ -268,6 +281,7 @@ export interface LanguageService {
268
281
  * 设置目标维基百科
269
282
  * @param wiki Wikipedia URL / 维基百科网址
270
283
  * @param user URI for wiki userpage or email address of the user / 维基用户页面地址或用户的电子邮件地址
284
+ * @since v1.18.1
271
285
  */
272
286
  setTargetWikipedia(wiki: string, user: string): Promise<void>;
273
287
  [Symbol.dispose](): void;
@@ -302,6 +316,7 @@ export interface Parser {
302
316
  *
303
317
  * 获取当前的解析设置
304
318
  * @param config unprocessed parser configuration / 未处理的解析设置
319
+ * @throws `RangeError` if the configuration file path or file name is invalid
305
320
  */
306
321
  getConfig(config?: ConfigData): Config;
307
322
  /**
package/dist/bin/cli.js CHANGED
@@ -285,11 +285,10 @@ if (recursive) {
285
285
  if (!fs_1.default.existsSync(file)) {
286
286
  return [];
287
287
  }
288
- if (fs_1.default.statSync(file).isFile()) {
289
- return file;
290
- }
291
- return fs_1.default.readdirSync(file, { recursive: true, withFileTypes: true }).filter(dir => dir.isFile())
292
- .map(dir => path_1.default.join(dir.parentPath, dir.name));
288
+ return fs_1.default.statSync(file).isFile()
289
+ ? file
290
+ : fs_1.default.readdirSync(file, { recursive: true, withFileTypes: true }).filter(dir => dir.isFile())
291
+ .map(dir => path_1.default.join(dir.parentPath, dir.name));
293
292
  });
294
293
  }
295
294
  if (exts.length > 0) {
@@ -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.47.0';
44
+ const pkg = 'wikilint', version = '2.48.0';
45
45
  /**
46
46
  * Get the parser configuration for a Wikimedia Foundation project.
47
47
  * @param site site nickname
@@ -49,6 +49,7 @@ const pkg = 'wikilint', version = '2.47.0';
49
49
  * @param user URI for wiki userpage or email address of the user
50
50
  * @param force whether to overwrite the existing configuration
51
51
  * @param internal for internal use
52
+ * @throws `RangeError` if the site nickname or script path is missing, or Extension:CodeMirror is not installed
52
53
  */
53
54
  exports.default = async (site, url, user, force, internal) => {
54
55
  // wrong calls
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ import type { TitleOptions } from './lib/title';
5
5
  import type { LanguageService, QuickFixData } from './lib/lsp';
6
6
  import type { Token } from './internal';
7
7
  declare interface Parser extends ParserBase {
8
- default: Parser;
8
+ default: this;
9
9
  /** @since v1.5.1 */
10
10
  readonly rules: readonly LintError.Rule[];
11
11
  configPaths: string[];
@@ -33,6 +33,7 @@ declare interface Parser extends ParserBase {
33
33
  *
34
34
  * 获取一个WMF网站的名称
35
35
  * @param url script path
36
+ * @throws `RangeError` if the URL is not a recognizable WMF site
36
37
  * @since v1.22.0
37
38
  */
38
39
  getWMFSite(url: string): [string, string];
@@ -43,6 +44,7 @@ declare interface Parser extends ParserBase {
43
44
  * @param site site nickname / 网站别名
44
45
  * @param url script path / 脚本路径
45
46
  * @param user URI for wiki userpage or email address of the user / 维基用户页面地址或用户的电子邮件地址
47
+ * @throws `RangeError` if Extension:CodeMirror is not installed
46
48
  * @since v1.18.4
47
49
  */
48
50
  fetchConfig(site: string, url: string, user?: string): Promise<Config>;
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ const re = new RegExp(String.raw `^https?:\/\/([^./]+)\.(${common_1.wmf})\.org`,
20
20
  /**
21
21
  * require一个JSON文件
22
22
  * @param file 文件名
23
- * @throws {RangeError} 仅支持JSON文件
23
+ * @throws `RangeError` 仅支持JSON文件
24
24
  */
25
25
  const jsonRequire = (file) => {
26
26
  const fullPath = require.resolve(file);
@@ -159,7 +159,7 @@ const Parser = {
159
159
  for (const key of keys) {
160
160
  const str = t[key];
161
161
  if (str?.includes('\0')) {
162
- const s = root.buildFromStr(str, constants_1.BuildMethod.Text);
162
+ const s = root.buildFromStr(str, 1 /* BuildMethod.Text */);
163
163
  if (key === 'main') {
164
164
  t.main = s;
165
165
  }
package/dist/lib/lsp.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import Parser from '../index';
2
2
  import { Token } from '../src/index';
3
- import type { Range, Position, ColorInformation, ColorPresentation, FoldingRange, DocumentLink, Location, DocumentHighlight, WorkspaceEdit, Diagnostic as DiagnosticBase, TextEdit, Hover, SignatureHelp, InlayHint, CodeAction, DocumentSymbol } from 'vscode-languageserver-types';
3
+ import type { Range, Position, ColorInformation, ColorPresentation, CompletionItem as CompletionItemBase, FoldingRange, DocumentLink, Location, DocumentHighlight, WorkspaceEdit, Diagnostic as DiagnosticBase, TextEdit, Hover, SignatureHelp, InlayHint, CodeAction, DocumentSymbol } from 'vscode-languageserver-types';
4
4
  import type { Config, LanguageService as LanguageServiceBase, CompletionItem, SignatureData } from '../base';
5
5
  import type { AttributeToken } from '../internal';
6
6
  import type { TokenTypeMap, SelectedTokenTypes } from '../map';
@@ -41,6 +41,14 @@ export declare class LanguageService implements LanguageServiceBase {
41
41
  * @param position position / 位置
42
42
  */
43
43
  provideCompletionItems(text: string, position: Position): Promise<CompletionItem[] | undefined>;
44
+ /**
45
+ * Add additional information to a completion item
46
+ *
47
+ * 为自动补全项添加额外信息
48
+ * @param item completion item / 自动补全项
49
+ * @since v1.48.0
50
+ */
51
+ resolveCompletionItem<T extends CompletionItem | CompletionItemBase>(item: T): T;
44
52
  /**
45
53
  * Provide grammar check
46
54
  *
package/dist/lib/lsp.js CHANGED
@@ -106,28 +106,33 @@ const createNodeRange = (token) => {
106
106
  * @param pos.line line number
107
107
  * @param pos.character character number
108
108
  * @param extra extra text
109
- * @param getDoc documentation method
110
109
  */
111
- const getCompletion = (words, kind, mt, { line, character }, extra = '', getDoc) => [...new Set(words)].map((w) => {
112
- const doc = getDoc?.(w)?.description;
113
- return {
114
- label: w,
115
- kind,
116
- textEdit: {
117
- range: {
118
- start: { line, character: character - mt.length },
119
- end: { line, character },
120
- },
121
- newText: w + extra,
122
- },
123
- ...doc && {
124
- documentation: {
125
- kind: 'markdown',
126
- value: doc,
127
- },
110
+ const getCompletion = (words, kind, mt, { line, character }, extra = '') => [...new Set(words)].map((w) => ({
111
+ label: w,
112
+ kind,
113
+ textEdit: {
114
+ range: {
115
+ start: { line, character: character - mt.length },
116
+ end: { line, character },
128
117
  },
129
- };
130
- });
118
+ newText: w + extra,
119
+ },
120
+ }));
121
+ /**
122
+ * Normalize a magic word.
123
+ * @param word magic word
124
+ * @param insensitive dictionary of insensitive magic words
125
+ * @param sensitive dictionary of sensitive magic words
126
+ */
127
+ const normalizeMagicWord = (word, insensitive, sensitive) => {
128
+ if (Object.hasOwn(insensitive, word)) {
129
+ word = insensitive[word];
130
+ }
131
+ else if (Object.hasOwn(sensitive, word)) {
132
+ word = sensitive[word];
133
+ }
134
+ return word.toLowerCase();
135
+ };
131
136
  /**
132
137
  * Get the caret position at the position from a word.
133
138
  * @param root root token
@@ -380,30 +385,42 @@ class LanguageService {
380
385
  /** @private */
381
386
  config;
382
387
  /** @private */
383
- data;
388
+ static data;
384
389
  /* NOT FOR BROWSER ONLY */
385
390
  /** @private */
386
391
  // @ts-expect-error lazy initialization
387
392
  lilypond;
388
393
  #lilypondData;
389
394
  #mathData;
395
+ static {
396
+ this.data = require(path_1.default.join('..', '..', 'data', 'signatures.json'));
397
+ }
390
398
  /* NOT FOR BROWSER ONLY END */
399
+ /**
400
+ * 检索状态开关
401
+ * @param name 魔术字名
402
+ */
403
+ static #getBehaviorSwitch(name) {
404
+ return this.data.behaviorSwitches.find(({ aliases }) => aliases.includes(name));
405
+ }
406
+ /**
407
+ * 检索解析器函数
408
+ * @param name 函数名
409
+ */
410
+ static #getParserFunction(name) {
411
+ return this.data.parserFunctions
412
+ .find(({ aliases }) => aliases.some(alias => alias.replace(/^#/u, '') === name));
413
+ }
391
414
  /** @param uri 任务标识 */
392
415
  constructor(uri) {
393
416
  exports.tasks.set(uri, this);
417
+ Object.defineProperties(this, {
418
+ config: { enumerable: false },
419
+ });
394
420
  /* NOT FOR BROWSER ONLY */
395
421
  const dataDir = path_1.default.join('..', '..', 'data'), extDir = path_1.default.join(dataDir, 'ext');
396
422
  this.#lilypondData = require(path_1.default.join(extDir, 'score.json'));
397
423
  this.#mathData = require(path_1.default.join(extDir, 'math.json'));
398
- /* NOT FOR BROWSER ONLY END */
399
- Object.defineProperties(this, {
400
- config: { enumerable: false },
401
- data: {
402
- enumerable: false,
403
- /* NOT FOR BROWSER ONLY */
404
- value: require(path_1.default.join(dataDir, 'signatures.json')),
405
- },
406
- });
407
424
  }
408
425
  /** @implements */
409
426
  async destroy() {
@@ -608,28 +625,14 @@ class LanguageService {
608
625
  * @param position position / 位置
609
626
  */
610
627
  async provideCompletionItems(text, position) {
611
- 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) ?? ''), [, , iAlias, sAlias] = this.config.doubleUnderscore;
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) ?? '');
612
629
  if (mt?.[1] !== undefined) { // tag
613
630
  const closing = mt[1].startsWith('/');
614
631
  return getCompletion(allTags, 'Class', mt[1].slice(closing ? 1 : 0), position, closing && !curLine?.slice(character).trimStart().startsWith('>') ? '>' : '');
615
632
  }
616
633
  else if (mt?.[4] || mt?.[5] && jaSwitches.length > 0) { // behavior switch
617
634
  const isJa = mt[5] !== undefined;
618
- return getCompletion(isJa ? jaSwitches : switches, 'Constant', mt[isJa ? 5 : 4], position, '', name => {
619
- if (!this.data) {
620
- return undefined;
621
- }
622
- else if (!isJa) {
623
- name = name.slice(2, -2);
624
- }
625
- if (Object.hasOwn(iAlias, name)) {
626
- name = iAlias[name];
627
- }
628
- else if (Object.hasOwn(sAlias, name)) {
629
- name = sAlias[name];
630
- }
631
- return this.#getBehaviorSwitch(name.toLowerCase());
632
- });
635
+ return getCompletion(isJa ? jaSwitches : switches, 'Constant', mt[isJa ? 5 : 4], position);
633
636
  }
634
637
  else if (mt?.[6] !== undefined) { // protocol
635
638
  return getCompletion(protocols, 'Reference', mt[6], position);
@@ -643,7 +646,7 @@ class LanguageService {
643
646
  return getCompletion(root.querySelectorAll('arg').filter(token => token.name && token !== cur)
644
647
  .map(({ name }) => name), 'Variable', match, position);
645
648
  }
646
- const [insensitive, sensitive] = this.config.parserFunction, colon = match.startsWith(':'), str = colon ? match.slice(1).trimStart() : match;
649
+ const colon = match.startsWith(':'), str = colon ? match.slice(1).trimStart() : match;
647
650
  if (mt[2] === '[[') { // link
648
651
  return getCompletion(root.querySelectorAll('link,file,category,redirect-target').filter(token => token !== cur).map(({ name }) => name), 'Folder', str, position);
649
652
  }
@@ -657,18 +660,7 @@ class LanguageService {
657
660
  words = functions.filter(s => s.endsWith(':')).map(s => s.slice(0, -1));
658
661
  }
659
662
  return [
660
- ...getCompletion(words, 'Function', match, position, '', name => {
661
- if (!this.data) {
662
- return undefined;
663
- }
664
- else if (Object.hasOwn(insensitive, name)) {
665
- name = insensitive[name];
666
- }
667
- else if (Object.hasOwn(sensitive, name)) {
668
- name = sensitive[name];
669
- }
670
- return this.#getParserFunction(name.toLowerCase());
671
- }),
663
+ ...getCompletion(words, 'Function', match, position),
672
664
  ...match.startsWith('#')
673
665
  ? []
674
666
  : getCompletion(root.querySelectorAll('template').filter(token => token !== cur).map(token => {
@@ -806,6 +798,36 @@ class LanguageService {
806
798
  }
807
799
  return undefined;
808
800
  }
801
+ /**
802
+ * Add additional information to a completion item
803
+ *
804
+ * 为自动补全项添加额外信息
805
+ * @param item completion item / 自动补全项
806
+ * @since v1.48.0
807
+ */
808
+ resolveCompletionItem(item) {
809
+ if (!LanguageService.data) {
810
+ return item;
811
+ }
812
+ this.config ??= index_1.default.getConfig();
813
+ const { kind } = item, Constant = 21, Function = 3;
814
+ let { label } = item, doc;
815
+ if (kind === 'Constant' || kind === Constant) {
816
+ const [, , insensitive, sensitive] = this.config.doubleUnderscore;
817
+ if (label.endsWith('_')) {
818
+ label = label.slice(2, -2);
819
+ }
820
+ doc = LanguageService.#getBehaviorSwitch(normalizeMagicWord(label, insensitive, sensitive));
821
+ }
822
+ else if ((kind === 'Function' || kind === Function) && !label.startsWith('/')) {
823
+ const [insensitive, sensitive] = this.config.parserFunction;
824
+ doc = LanguageService.#getParserFunction(normalizeMagicWord(label, insensitive, sensitive));
825
+ }
826
+ if (doc?.description) {
827
+ item.documentation = { kind: 'markdown', value: doc.description };
828
+ }
829
+ return item;
830
+ }
809
831
  /**
810
832
  * Provide grammar check
811
833
  *
@@ -1157,26 +1179,25 @@ class LanguageService {
1157
1179
  */
1158
1180
  async provideDocumentHighlights(text, position) {
1159
1181
  const root = await this.#queue(text), index = root.indexFromPos(position.line, position.character), node = root.elementFromIndex(index), type = node?.type;
1160
- if (!node || (type === 'ext' || type === 'html') && !/\w/u.test(text.charAt(index))) {
1161
- return undefined;
1162
- }
1163
- return getMatchingTokens(root, node, highlightTypes)?.map((token) => {
1164
- const range = createNodeRange(token);
1165
- if (type === 'ext' || type === 'html') {
1166
- const { start } = range;
1167
- start.character += token.closing ? 2 : 1;
1168
- return {
1169
- range: {
1170
- start,
1171
- end: {
1172
- line: start.line,
1173
- character: start.character + token.name.length,
1182
+ return !node || (type === 'ext' || type === 'html') && !/\w/u.test(text.charAt(index))
1183
+ ? undefined
1184
+ : getMatchingTokens(root, node, highlightTypes)?.map((token) => {
1185
+ const range = createNodeRange(token);
1186
+ if (type === 'ext' || type === 'html') {
1187
+ const { start } = range;
1188
+ start.character += token.closing ? 2 : 1;
1189
+ return {
1190
+ range: {
1191
+ start,
1192
+ end: {
1193
+ line: start.line,
1194
+ character: start.character + token.name.length,
1195
+ },
1174
1196
  },
1175
- },
1176
- };
1177
- }
1178
- return { range };
1179
- });
1197
+ };
1198
+ }
1199
+ return { range };
1200
+ });
1180
1201
  }
1181
1202
  /**
1182
1203
  * Provide references
@@ -1281,21 +1302,6 @@ class LanguageService {
1281
1302
  },
1282
1303
  };
1283
1304
  }
1284
- /**
1285
- * 检索状态开关
1286
- * @param name 魔术字名
1287
- */
1288
- #getBehaviorSwitch(name) {
1289
- return this.data.behaviorSwitches.find(({ aliases }) => aliases.includes(name));
1290
- }
1291
- /**
1292
- * 检索解析器函数
1293
- * @param name 函数名
1294
- */
1295
- #getParserFunction(name) {
1296
- return this.data.parserFunctions
1297
- .find(({ aliases }) => aliases.some(alias => alias.replace(/^#/u, '') === name));
1298
- }
1299
1305
  /**
1300
1306
  * Provide hover information
1301
1307
  *
@@ -1305,7 +1311,7 @@ class LanguageService {
1305
1311
  */
1306
1312
  async provideHover(text, position) {
1307
1313
  /* c8 ignore next 3 */
1308
- if (!this.data) {
1314
+ if (!LanguageService.data) {
1309
1315
  return undefined;
1310
1316
  }
1311
1317
  const root = await this.#queue(text);
@@ -1317,23 +1323,23 @@ class LanguageService {
1317
1323
  const { type, parentNode, length, name } = offsetNode;
1318
1324
  let info, f, colon, range;
1319
1325
  if (offsetNode.is('double-underscore') && offset > 0) {
1320
- info = this.#getBehaviorSwitch(offsetNode.name);
1326
+ info = LanguageService.#getBehaviorSwitch(offsetNode.name);
1321
1327
  }
1322
1328
  else if (type === 'magic-word-name') {
1323
- info = this.#getParserFunction(parentNode.name);
1329
+ info = LanguageService.#getParserFunction(parentNode.name);
1324
1330
  f = offsetNode.text().trim();
1325
1331
  colon = parentNode.getAttribute('colon');
1326
1332
  }
1327
1333
  else if (length === 1 && offsetNode.is('magic-word') && !offsetNode.modifier
1328
1334
  && (offset > 0 || root.posFromIndex(offsetNode.getAbsoluteIndex()).left === position.character)) {
1329
- info = this.#getParserFunction(name);
1335
+ info = LanguageService.#getParserFunction(name);
1330
1336
  f = offsetNode.firstChild.text().trim();
1331
1337
  colon = offsetNode.getAttribute('colon');
1332
1338
  }
1333
1339
  else if ((offsetNode.is('magic-word') || offsetNode.is('template'))
1334
1340
  && offsetNode.modifier && offset >= 2 && offsetNode.getRelativeIndex(0) > offset) {
1335
1341
  f = offsetNode.modifier.trim().slice(0, -1);
1336
- info = this.#getParserFunction(f.toLowerCase());
1342
+ info = LanguageService.#getParserFunction(f.toLowerCase());
1337
1343
  colon = ':';
1338
1344
  if (info) {
1339
1345
  const aIndex = offsetNode.getAbsoluteIndex();
@@ -1403,14 +1409,14 @@ class LanguageService {
1403
1409
  */
1404
1410
  async provideSignatureHelp(text, position) {
1405
1411
  /* c8 ignore next 3 */
1406
- if (!this.data) {
1412
+ if (!LanguageService.data) {
1407
1413
  return undefined;
1408
1414
  }
1409
1415
  const { line, character } = position, curLine = text.split(/\r?\n/u, line + 1)[line], { lastChild } = await this.#queueSignature(`${curLine.slice(0, character + (0, common_1.numLeadingSpaces)(curLine.slice(character), /[{}<]|$/u))}}}`);
1410
1416
  if (!lastChild.is('magic-word') || lastChild.length === 1) {
1411
1417
  return undefined;
1412
1418
  }
1413
- const { name, childNodes, firstChild } = lastChild, info = this.#getParserFunction(name);
1419
+ const { name, childNodes, firstChild } = lastChild, info = LanguageService.#getParserFunction(name);
1414
1420
  if (!info?.signatures) {
1415
1421
  return undefined;
1416
1422
  }
package/dist/lib/node.js CHANGED
@@ -90,7 +90,7 @@ let AstNode = (() => {
90
90
  /** @private */
91
91
  getChildNodes() {
92
92
  const { childNodes } = this;
93
- if (Object.isFrozen(childNodes)) {
93
+ if (Object.isFrozen(childNodes)) { // NOT FOR TEMPLATE
94
94
  return [...childNodes];
95
95
  }
96
96
  return childNodes;
@@ -24,8 +24,7 @@ export declare class AstText extends AstNode {
24
24
  *
25
25
  * 将文本子节点分裂为两部分
26
26
  * @param offset position to be splitted at / 分裂位置
27
- * @throws `RangeError` 错误的断开位置
28
- * @throws `Error` 没有父节点
27
+ * @throws `Error` if the text node has no parent node
29
28
  */
30
29
  splitText(offset: number): AstText;
31
30
  /**
@@ -33,7 +32,7 @@ export declare class AstText extends AstNode {
33
32
  *
34
33
  * 转义 `=` 和 `|`
35
34
  * @since v1.1.4
36
- * @throws `Error` 没有父节点
35
+ * @throws `Error` if the text node has no parent node
37
36
  */
38
37
  escape(): void;
39
38
  }
package/dist/lib/text.js CHANGED
@@ -70,7 +70,7 @@ class AstText extends node_1.AstNode {
70
70
  }
71
71
  /** @private */
72
72
  toString(skip) {
73
- if (skip && !this.parentNode?.getAttribute('built')) {
73
+ if (skip && !this.parentNode?.getAttribute('built')) { // NOT FOR TEMPLATE
74
74
  return (0, string_1.removeComment)(this.data);
75
75
  }
76
76
  return this.data;
@@ -249,7 +249,7 @@ class AstText extends node_1.AstNode {
249
249
  if (!/(?:^|[^=])=+\s*(?:\S\s*)?$/u.test(line)) {
250
250
  continue;
251
251
  }
252
- if (lineEnd === -1) {
252
+ else if (lineEnd === -1) {
253
253
  endIndex = sibling.getAbsoluteIndex() + line.length;
254
254
  ({ top: endLine, left: endCol } = root.posFromIndex(endIndex));
255
255
  }
@@ -322,8 +322,7 @@ class AstText extends node_1.AstNode {
322
322
  *
323
323
  * 将文本子节点分裂为两部分
324
324
  * @param offset position to be splitted at / 分裂位置
325
- * @throws `RangeError` 错误的断开位置
326
- * @throws `Error` 没有父节点
325
+ * @throws `Error` if the text node has no parent node
327
326
  */
328
327
  splitText(offset) {
329
328
  LSP: {
@@ -343,7 +342,7 @@ class AstText extends node_1.AstNode {
343
342
  *
344
343
  * 转义 `=` 和 `|`
345
344
  * @since v1.1.4
346
- * @throws `Error` 没有父节点
345
+ * @throws `Error` if the text node has no parent node
347
346
  */
348
347
  escape() {
349
348
  LSP: {
@@ -57,8 +57,7 @@ const getSymbol = (s) => {
57
57
  * @param wikitext
58
58
  * @param config
59
59
  * @param accum
60
- * @throws `RangeError` Maximum iteration exceeded
61
- * @throws `TranscludeToken.constructor()`
60
+ * @throws `RangeError` if maximum iteration exceeded
62
61
  */
63
62
  const parseBraces = (wikitext, config, accum) => {
64
63
  const source = String.raw `${config.excludes.includes('heading') ? '' : String.raw `^((?:\0\d+[cno]\x7F)*)={1,6}|`}\[\[|-\{(?!\{)`, [, , , subst] = config.parserFunction, stack = [], linkStack = [];
@@ -91,7 +90,7 @@ const parseBraces = (wikitext, config, accum) => {
91
90
  }
92
91
  catch (e) {
93
92
  /* c8 ignore next 3 */
94
- if (!(e instanceof SyntaxError) || e.message !== 'Invalid template name') {
93
+ if (!(e instanceof SyntaxError) || e.message !== transclude_1.invalidTemplateMsg) {
95
94
  throw e;
96
95
  }
97
96
  }
@@ -170,7 +169,7 @@ const parseBraces = (wikitext, config, accum) => {
170
169
  }
171
170
  catch (e) {
172
171
  /* c8 ignore next 3 */
173
- if (!(e instanceof SyntaxError && e.message === 'Invalid template name')) {
172
+ if (!(e instanceof SyntaxError && e.message === transclude_1.invalidTemplateMsg)) {
174
173
  throw e;
175
174
  }
176
175
  skip = true;
@@ -99,7 +99,7 @@ class AttributeToken extends index_2.Token {
99
99
  /** @private */
100
100
  afterBuild() {
101
101
  if (this.#equal.includes('\0')) {
102
- this.#equal = this.buildFromStr(this.#equal, constants_1.BuildMethod.String);
102
+ this.#equal = this.buildFromStr(this.#equal, 0 /* BuildMethod.String */);
103
103
  }
104
104
  if (this.parentNode) {
105
105
  this.#tag = this.parentNode.name;
@@ -126,21 +126,22 @@ let ConverterFlagsToken = (() => {
126
126
  const rect = new rect_1.BoundingRect(this, start), { childNodes, length } = this;
127
127
  for (let i = length - 1; i >= 0; i--) {
128
128
  const child = childNodes[i], flag = child.text().trim();
129
- if (flag
130
- && !variantFlags.has(flag.toLowerCase())
131
- && !unknownFlags.has(flag)
132
- && (variantFlags.size > 0 || !definedFlags.has(flag))) {
133
- const e = (0, lint_1.generateForChild)(child, rect, rule, 'invalid-conversion-flag', s);
134
- if (computeEditInfo || fix) {
135
- if (variantFlags.size === 0 && definedFlags.has(flag.toUpperCase())) {
136
- e.fix = (0, lint_1.fixByUpper)(e, flag);
137
- }
138
- else if (computeEditInfo) {
139
- e.suggestions = [(0, lint_1.fixByRemove)(e, i && -1)];
140
- }
129
+ if (!flag
130
+ || variantFlags.has(flag.toLowerCase())
131
+ || unknownFlags.has(flag)
132
+ || variantFlags.size === 0 && definedFlags.has(flag)) {
133
+ continue;
134
+ }
135
+ const e = (0, lint_1.generateForChild)(child, rect, rule, 'invalid-conversion-flag', s);
136
+ if (computeEditInfo || fix) {
137
+ if (variantFlags.size === 0 && definedFlags.has(flag.toUpperCase())) {
138
+ e.fix = (0, lint_1.fixByUpper)(e, flag);
139
+ }
140
+ else if (computeEditInfo) {
141
+ e.suggestions = [(0, lint_1.fixByRemove)(e, i && -1)];
141
142
  }
142
- errors.push(e);
143
143
  }
144
+ errors.push(e);
144
145
  }
145
146
  }
146
147
  return errors;
@@ -91,6 +91,7 @@ let ExtLinkToken = (() => {
91
91
  }
92
92
  /** @private */
93
93
  toString(skip) {
94
+ // eslint-disable-next-line unicorn/prefer-ternary
94
95
  if (this.length === 1) {
95
96
  return `[${super.toString(skip)}${this.#space}]`;
96
97
  }
package/dist/src/index.js CHANGED
@@ -175,10 +175,10 @@ class Token extends element_1.AstElement {
175
175
  const n = Number(s.slice(0, -1));
176
176
  return this.#accum[n];
177
177
  }).filter(node => node !== '');
178
- if (type === constants_1.BuildMethod.String) {
178
+ if (type === 0 /* BuildMethod.String */) {
179
179
  return nodes.map(String).join('');
180
180
  }
181
- else if (type === constants_1.BuildMethod.Text) {
181
+ else if (type === 1 /* BuildMethod.Text */) {
182
182
  return (0, string_1.text)(nodes);
183
183
  }
184
184
  return nodes;
@@ -103,7 +103,7 @@ let LinkBaseToken = (() => {
103
103
  afterBuild() {
104
104
  this.#title = this.getTitle();
105
105
  if (this.#delimiter.includes('\0')) {
106
- this.#delimiter = this.buildFromStr(this.#delimiter, constants_1.BuildMethod.String);
106
+ this.#delimiter = this.buildFromStr(this.#delimiter, 0 /* BuildMethod.String */);
107
107
  }
108
108
  this.#setName();
109
109
  super.afterBuild();
@@ -124,7 +124,7 @@ let LinkBaseToken = (() => {
124
124
  /** @private */
125
125
  toString(skip) {
126
126
  const str = super.toString(skip, this.#delimiter);
127
- if (this.#bracket) {
127
+ if (this.#bracket) { // NOT FOR TEMPLATE
128
128
  return `[[${str}]]`;
129
129
  }
130
130
  return str;
@@ -137,7 +137,7 @@ let LinkBaseToken = (() => {
137
137
  'trim']()
138
138
  : `${target.trim()}|${(0, string_1.text)(childNodes.slice(1)
139
139
  .filter(({ type: t, name }) => t !== 'image-parameter' || name !== 'invalid'), '|')}`;
140
- if (this.#bracket) {
140
+ if (this.#bracket) { // NOT FOR TEMPLATE
141
141
  return `[[${str}]]`;
142
142
  }
143
143
  return str;
@@ -80,10 +80,9 @@ let CategorytreeToken = (() => {
80
80
  lint(start = this.getAbsoluteIndex()) {
81
81
  LINT: {
82
82
  const rule = 'no-ignored', s = index_1.default.lintConfig.getSeverity(rule, 'categorytree');
83
- if (s && this.#lint()) {
84
- return [(0, lint_1.generateForSelf)(this, { start }, rule, 'invalid-category', s)];
85
- }
86
- return super.lint(start, false);
83
+ return s && this.#lint()
84
+ ? [(0, lint_1.generateForSelf)(this, { start }, rule, 'invalid-category', s)]
85
+ : super.lint(start, false);
87
86
  }
88
87
  }
89
88
  /** 无效的分类名 */
@@ -177,14 +177,15 @@ class FileToken extends base_1.LinkBaseToken {
177
177
  ...relevantArgs.slice(-1),
178
178
  ];
179
179
  }
180
- if (relevantArgs.length > 1) {
181
- let severity = !isCaption || !extension || constants_1.extensions.has(extension);
182
- if (isCaption && severity) {
183
- const plainArgs = filterArgs(relevantArgs, transclusion);
184
- severity = plainArgs.length > 1 && ((arg) => plainArgs.includes(arg));
185
- }
186
- Array.prototype.push.apply(errors, generate(relevantArgs, 'duplicate', key, severity));
180
+ if (relevantArgs.length < 2) {
181
+ continue;
182
+ }
183
+ let severity = !isCaption || !extension || constants_1.extensions.has(extension);
184
+ if (isCaption && severity) {
185
+ const plainArgs = filterArgs(relevantArgs, transclusion);
186
+ severity = plainArgs.length > 1 && ((arg) => plainArgs.includes(arg));
187
187
  }
188
+ Array.prototype.push.apply(errors, generate(relevantArgs, 'duplicate', key, severity));
188
189
  }
189
190
  if (frameKeys.length > 1) {
190
191
  Array.prototype.push.apply(errors, generate(args.filter(({ name }) => frame.has(name)), 'conflicting', 'frame'));
@@ -55,5 +55,5 @@ export declare abstract class TableToken extends TrBaseToken {
55
55
  * 获取第n行
56
56
  * @param n row number / 行号
57
57
  */
58
- getNthRow(n: number, force?: boolean, insert?: false): TrToken | this | undefined;
58
+ getNthRow(n: number, force?: boolean, insert?: false): TrToken | this;
59
59
  }
@@ -43,7 +43,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
43
43
  Object.defineProperty(exports, "__esModule", { value: true });
44
44
  exports.TdToken = void 0;
45
45
  const lint_1 = require("../../util/lint");
46
- const constants_1 = require("../../util/constants");
47
46
  const rect_1 = require("../../lib/rect");
48
47
  const cached_1 = require("../../mixin/cached");
49
48
  const index_1 = __importDefault(require("../../index"));
@@ -124,7 +123,7 @@ let TdToken = (() => {
124
123
  /** @private */
125
124
  afterBuild() {
126
125
  if (this.#innerSyntax.includes('\0')) {
127
- this.#innerSyntax = this.buildFromStr(this.#innerSyntax, constants_1.BuildMethod.String);
126
+ this.#innerSyntax = this.buildFromStr(this.#innerSyntax, 0 /* BuildMethod.String */);
128
127
  }
129
128
  super.afterBuild();
130
129
  }
@@ -140,6 +139,7 @@ let TdToken = (() => {
140
139
  }
141
140
  /** @private */
142
141
  getGaps(i) {
142
+ // eslint-disable-next-line unicorn/prefer-ternary
143
143
  if (i === 1) {
144
144
  return this.#innerSyntax.length;
145
145
  }
@@ -3,6 +3,7 @@ import { ParameterToken } from './parameter';
3
3
  import { AtomToken } from './atom';
4
4
  import { SyntaxToken } from './syntax';
5
5
  import type { Config, LintError } from '../base';
6
+ export declare const invalidTemplateMsg = "Invalid template name";
6
7
  /**
7
8
  * template or magic word
8
9
  *
@@ -78,7 +79,7 @@ export declare abstract class TranscludeToken extends Token {
78
79
  * Get possible values of some magic words
79
80
  *
80
81
  * 对特定魔术字获取可能的取值
81
- * @throws `Error` 不是可接受的魔术字
82
+ * @throws `Error` if not a supported magic word
82
83
  */
83
84
  getPossibleValues(): Token[];
84
85
  }
@@ -37,11 +37,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
38
  };
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
- exports.TranscludeToken = void 0;
40
+ exports.TranscludeToken = exports.invalidTemplateMsg = void 0;
41
41
  const string_1 = require("../util/string");
42
42
  const lint_1 = require("../util/lint");
43
43
  const debug_1 = require("../util/debug");
44
- const constants_1 = require("../util/constants");
45
44
  const rect_1 = require("../lib/rect");
46
45
  const gapped_1 = require("../mixin/gapped");
47
46
  const noEscape_1 = require("../mixin/noEscape");
@@ -50,6 +49,8 @@ const index_2 = require("./index");
50
49
  const parameter_1 = require("./parameter");
51
50
  const atom_1 = require("./atom");
52
51
  const syntax_1 = require("./syntax");
52
+ exports.invalidTemplateMsg = 'Invalid template name';
53
+ const sortArgs = /** @ignore */ ({ childNodes }, args) => [...args].sort((a, b) => childNodes.indexOf(a) - childNodes.indexOf(b));
53
54
  /**
54
55
  * template or magic word
55
56
  *
@@ -159,7 +160,7 @@ let TranscludeToken = (() => {
159
160
  const name = (0, string_1.removeComment)(title).trim();
160
161
  if (!this.normalizeTitle(name, 10, { halfParsed: true, temporary: true }).valid) {
161
162
  accum.pop();
162
- throw new SyntaxError('Invalid template name');
163
+ throw new SyntaxError(exports.invalidTemplateMsg);
163
164
  }
164
165
  const token = new atom_1.AtomToken(title, 'template-name', config, accum, {});
165
166
  super.insertAt(token);
@@ -226,7 +227,7 @@ let TranscludeToken = (() => {
226
227
  /** @private */
227
228
  afterBuild() {
228
229
  if (this.modifier.includes('\0')) {
229
- this.setAttribute('modifier', this.buildFromStr(this.modifier, constants_1.BuildMethod.String));
230
+ this.setAttribute('modifier', this.buildFromStr(this.modifier, 0 /* BuildMethod.String */));
230
231
  }
231
232
  super.afterBuild();
232
233
  if (this.isTemplate()) {
@@ -371,6 +372,9 @@ let TranscludeToken = (() => {
371
372
  args = new Set();
372
373
  this.#args.set(keyStr, args);
373
374
  }
375
+ if (copy) {
376
+ args = new Set(sortArgs(this, args));
377
+ }
374
378
  return args;
375
379
  }
376
380
  /**
@@ -382,7 +386,7 @@ let TranscludeToken = (() => {
382
386
  LINT: {
383
387
  const isTemplate = this.isTemplate();
384
388
  const duplicatedArgs = [...this.#args].filter(([, { size }]) => size > 1)
385
- .map(([key, args]) => [key, [...args]]);
389
+ .map(([key, args]) => [key, sortArgs(this, args)]);
386
390
  return isTemplate
387
391
  ? duplicatedArgs
388
392
  : duplicatedArgs
@@ -394,7 +398,7 @@ let TranscludeToken = (() => {
394
398
  * Get possible values of some magic words
395
399
  *
396
400
  * 对特定魔术字获取可能的取值
397
- * @throws `Error` 不是可接受的魔术字
401
+ * @throws `Error` if not a supported magic word
398
402
  */
399
403
  getPossibleValues() {
400
404
  const { type, name, childNodes, length: l } = this;
@@ -1,12 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.mathTags = exports.jsonTags = exports.extensions = exports.galleryParams = exports.enMsg = exports.BuildMethod = exports.MAX_STAGE = void 0;
3
+ exports.mathTags = exports.jsonTags = exports.extensions = exports.galleryParams = exports.enMsg = 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 = {}));
10
5
  exports.enMsg = (() => {
11
6
  LSP: return require(
12
7
  // eslint-disable-next-line n/no-missing-require
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikilint",
3
- "version": "2.47.0",
3
+ "version": "2.48.0",
4
4
  "description": "A Node.js linter for MediaWiki markup",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -46,7 +46,7 @@
46
46
  "build": "npm run build:core",
47
47
  "diff": "bash diff.sh",
48
48
  "diff:stat": "f() { git diff --stat -w --color=always $1 $2 -- . | grep '\\.ts'; }; f",
49
- "lint:ts": "tsc --noEmit && eslint --cache .",
49
+ "lint:ts": "[ ! -f dist/bin/config.js ] && npm run build:core; tsc --noEmit && eslint --cache .",
50
50
  "lint:json": "v8r -s config/.schema.json config/*.json && v8r -s data/.schema.json data/*.json && testUtil dist/test/json.js",
51
51
  "lint:md": "markdownlint-cli2 '*.md'",
52
52
  "lint": "npm run lint:ts && npm run lint:json && npm run lint:md",
@@ -102,26 +102,26 @@
102
102
  "@bhsd/common": "^4.1.0",
103
103
  "@bhsd/stylelint-util": "^1.1.0",
104
104
  "culori": "^4.0.2",
105
- "entities": "^8.0.0",
105
+ "entities": "^8.1.0",
106
106
  "vscode-languageserver-types": "^3.18.3"
107
107
  },
108
108
  "devDependencies": {
109
- "@bhsd/code-standard": "^4.2.0",
109
+ "@bhsd/code-standard": "^4.3.0",
110
110
  "@bhsd/nodejs": "^1.2.1",
111
- "@bhsd/test-util": "^3.2.0",
111
+ "@bhsd/test-util": "^3.2.1",
112
112
  "c8": "^12.0.0",
113
113
  "diff2html-cli": "^5.2.15",
114
114
  "esbuild": "^0.28.2",
115
- "eslint": "^10.10.0",
116
- "markdownlint-cli2": "^0.23.2",
115
+ "eslint": "^10.11.0",
116
+ "markdownlint-cli2": "^0.23.3",
117
117
  "mathoid-texvcjs": "^0.6.0",
118
- "stylelint": "^17.15.0",
118
+ "stylelint": "^17.16.0",
119
119
  "typescript": "^6.0.3",
120
120
  "v8r": "^6.1.0",
121
121
  "vscode-css-languageservice": "^6.3.10",
122
122
  "vscode-html-languageservice": "^5.6.2",
123
123
  "vscode-json-languageservice": "^5.7.2",
124
- "vscode-languageserver-textdocument": "^1.0.14"
124
+ "vscode-languageserver-textdocument": "^1.0.15"
125
125
  },
126
126
  "engines": {
127
127
  "node": "^20.19.0 || ^22.13.0 || >=24.11.0"