wikilint 2.35.2 → 2.37.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/LICENSE CHANGED
@@ -1,3 +1,21 @@
1
+ WikiLint
2
+ Copyright (C) 2022 Bhsd
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU General Public License as published by
6
+ the Free Software Foundation, either version 3 of the License, or
7
+ (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License
15
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
16
+
17
+
18
+
1
19
  GNU GENERAL PUBLIC LICENSE
2
20
  Version 3, 29 June 2007
3
21
 
package/dist/bin/cli.js CHANGED
@@ -78,16 +78,8 @@ const throwOnLintConfig = (config) => {
78
78
  * load lintConfig
79
79
  * @param file lintConfig file path
80
80
  */
81
- const loadLintConfig = async (file) => {
82
- const symbol = Symbol('lintConfig');
83
- let lintConfig = symbol;
84
- try {
85
- lintConfig = require(file);
86
- }
87
- catch { }
88
- if (lintConfig === symbol) {
89
- lintConfig = await import(file);
90
- }
81
+ const loadLintConfig = (file) => {
82
+ let lintConfig = require(file);
91
83
  if (lintConfig && typeof lintConfig === 'object' && 'default' in lintConfig) {
92
84
  lintConfig = lintConfig.default;
93
85
  }
@@ -292,42 +284,28 @@ config = (config.includes('/')
292
284
  ? path_1.default.resolve(index_1.default.config)
293
285
  : path_1.default.join(configPath, index_1.default.config)) + (config.endsWith('.json') ? '' : '.json');
294
286
  const { mtimeMs } = fs_1.default.statSync(config), obj = cache?.[include ? 'include' : 'noinclude'];
295
- let minimatch;
296
- try {
297
- ({ minimatch } = require('minimatch'));
298
- }
299
- catch {
300
- /* eslint-disable n/no-unsupported-features/node-builtins */
301
- if (typeof path_1.default.matchesGlob !== 'function') {
302
- exit('Cannot load Node.js package "minimatch"');
303
- }
304
- minimatch = path_1.default.matchesGlob.bind(path_1.default);
305
- /* eslint-enable n/no-unsupported-features/node-builtins */
306
- }
307
287
  (async () => {
308
288
  if (lintConfigFile) {
309
289
  try {
310
- index_1.default.lintConfig = await loadLintConfig(lintConfigFile);
290
+ index_1.default.lintConfig = loadLintConfig(lintConfigFile);
311
291
  }
312
292
  catch {
313
293
  exit(`Cannot load lint config file: ${lintConfigFile}`);
314
294
  }
315
295
  }
316
296
  else {
317
- await (async () => {
318
- let cur = process.cwd(), last;
319
- while (last !== cur) {
320
- for (const file of lintConfigDefaults) {
321
- try {
322
- index_1.default.lintConfig = await loadLintConfig(path_1.default.join(cur, file));
323
- return;
324
- }
325
- catch { }
297
+ let cur = process.cwd(), last;
298
+ while (last !== cur) {
299
+ for (const file of lintConfigDefaults) {
300
+ try {
301
+ index_1.default.lintConfig = loadLintConfig(path_1.default.join(cur, file));
302
+ return;
326
303
  }
327
- last = cur;
328
- cur = path_1.default.dirname(cur);
304
+ catch { }
329
305
  }
330
- })();
306
+ last = cur;
307
+ cur = path_1.default.dirname(cur);
308
+ }
331
309
  }
332
310
  index_1.default.lintConfig.computeEditInfo = false;
333
311
  index_1.default.lintConfig.fix = true;
@@ -338,7 +316,7 @@ catch {
338
316
  exiting = true;
339
317
  continue;
340
318
  }
341
- else if (ignorePatterns.some(ignore => minimatch(file, ignore))) {
319
+ else if (ignorePatterns.some(ignore => path_1.default.matchesGlob(file, ignore))) {
342
320
  continue;
343
321
  }
344
322
  const fileCache = obj?.[file];
@@ -39,46 +39,7 @@ const filterGadget = (id) => {
39
39
  const n = Number(id);
40
40
  return n < 2300 || n > 2303; // Gadget, Gadget talk, Gadget definition, Gadget definition talk
41
41
  };
42
- /**
43
- * Execute the data script.
44
- * @param obj MediaWiki module implementation
45
- */
46
- const execute = (obj) => {
47
- Object.entries(obj.files).find(([k]) => k.endsWith('.data.js'))[1]();
48
- };
49
- const mw = {
50
- loader: {
51
- done: false,
52
- /** @ignore */
53
- impl(callback) {
54
- execute(callback()[1]);
55
- },
56
- /** @ignore */
57
- implement(name, callback) {
58
- if (typeof callback === 'object') {
59
- execute(callback);
60
- }
61
- else if (!this.done) {
62
- callback();
63
- }
64
- if (name.startsWith('ext.CodeMirror.data')) {
65
- this.done = true;
66
- }
67
- },
68
- /** @ignore */
69
- state() {
70
- //
71
- },
72
- },
73
- config: {
74
- /** @ignore */
75
- set({ extCodeMirrorConfig }) {
76
- mwConfig = extCodeMirrorConfig;
77
- },
78
- },
79
- };
80
- const pkg = "wikilint", version = "2.35.2";
81
- let mwConfig;
42
+ const pkg = "wikilint", version = "2.37.0";
82
43
  /**
83
44
  * Get the parser configuration for a Wikimedia Foundation project.
84
45
  * @param site site nickname
@@ -122,15 +83,28 @@ exports.default = async (site, url, user, force, internal) => {
122
83
  siprop: 'general|magicwords|functionhooks|namespaces|namespacealiases',
123
84
  format: 'json',
124
85
  formatversion: '2',
125
- }, { general: { articlepath, variants, langconversion }, magicwords, namespaces, namespacealiases, functionhooks, } = (await (await fetch(`${url}/api.php?${new URLSearchParams(params).toString()}`, headers)).json()).query;
126
- try {
127
- eval(m); // eslint-disable-line no-eval
86
+ }, { general: { articlepath, variants, langconversion }, magicwords, namespaces, namespacealiases, functionhooks, } = (await (await fetch(`${url}/api.php?${new URLSearchParams(params).toString()}`, headers)).json()).query, tempFile = path_1.default.join(__dirname, 'mw.js');
87
+ fs_1.default.writeFileSync(tempFile, m);
88
+ const { stdout, stderr } = (0, child_process_1.spawnSync)(process.execPath, [
89
+ '-r',
90
+ './env.js',
91
+ process.allowedNodeEnvironmentFlags.has('--permission')
92
+ ? '--permission'
93
+ : '--experimental-permission',
94
+ `--allow-fs-read=${__dirname}`,
95
+ '--disable-warning=ExperimentalWarning',
96
+ tempFile,
97
+ ], { cwd: __dirname, encoding: 'utf8' });
98
+ fs_1.default.unlinkSync(tempFile);
99
+ if (stderr) {
100
+ console.error(stderr);
101
+ throw new Error('Failed to execute the fetched MediaWiki module!', { cause: m });
128
102
  }
129
- catch (e) {
130
- console.log(m);
131
- throw e;
103
+ let mwConfig;
104
+ try {
105
+ mwConfig = JSON.parse(stdout);
132
106
  }
133
- if (!mwConfig) {
107
+ catch {
134
108
  throw new RangeError('Extension:CodeMirror is not installed!');
135
109
  }
136
110
  const ns = Object.entries(namespaces).filter(([id]) => filterGadget(id))
@@ -138,7 +112,7 @@ exports.default = async (site, url, user, force, internal) => {
138
112
  [id, name],
139
113
  ...name === canonical ? [] : [[id, canonical]],
140
114
  ]), config = {
141
- ...(0, cm_util_1.getParserConfig)(require(path_1.default.join(dir, 'minimum')), mwConfig),
115
+ ...(0, cm_util_1.getParserConfig)(require(path_1.default.join(dir, 'minimum.json')), mwConfig),
142
116
  ...(0, cm_util_1.getKeywords)(magicwords),
143
117
  variants: langconversion ? (0, cm_util_1.getVariants)(variants) : [],
144
118
  namespaces: Object.fromEntries(ns),
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Execute the data script.
5
+ * @param obj MediaWiki module implementation
6
+ */
7
+ const execute = (obj) => {
8
+ Object.entries(obj.files).find(([k]) => k.endsWith('.data.js'))[1]();
9
+ };
10
+ Object.assign(globalThis, {
11
+ mw: {
12
+ loader: {
13
+ done: false,
14
+ /** @ignore */
15
+ impl(callback) {
16
+ execute(callback()[1]);
17
+ },
18
+ /** @ignore */
19
+ implement(name, callback) {
20
+ if (typeof callback === 'object') {
21
+ execute(callback);
22
+ }
23
+ else if (!this.done) {
24
+ callback();
25
+ }
26
+ if (name.startsWith('ext.CodeMirror.data')) {
27
+ this.done = true;
28
+ }
29
+ },
30
+ /** @ignore */
31
+ state() {
32
+ //
33
+ },
34
+ },
35
+ config: {
36
+ /** @ignore */
37
+ set({ extCodeMirrorConfig }) {
38
+ console.log(JSON.stringify(extCodeMirrorConfig));
39
+ },
40
+ },
41
+ },
42
+ });
package/dist/index.js CHANGED
@@ -16,12 +16,24 @@ const common_1 = require("@bhsd/common");
16
16
  const diff_1 = require("./util/diff");
17
17
  /* NOT FOR BROWSER ONLY */
18
18
  const re = new RegExp(String.raw `^https?:\/\/([^./]+)\.(${common_1.wmf})\.org`, 'iu');
19
+ /**
20
+ * require一个JSON文件
21
+ * @param file 文件名
22
+ * @throws {RangeError} 仅支持JSON文件
23
+ */
24
+ const jsonRequire = (file) => {
25
+ const fullPath = require.resolve(file);
26
+ if (fullPath.endsWith('.json')) {
27
+ return require(fullPath);
28
+ }
29
+ throw new RangeError('Only JSON files are supported!');
30
+ };
19
31
  /**
20
32
  * 从根路径require
21
33
  * @param file 文件名
22
34
  * @param dir 子路径
23
35
  */
24
- const rootRequire = (file, dir) => require(path_1.default.isAbsolute(file)
36
+ const rootRequire = (file, dir) => jsonRequire(path_1.default.isAbsolute(file)
25
37
  ? /* c8 ignore next */ file
26
38
  : path_1.default.join('..', file.includes('/') ? '' : dir, file));
27
39
  /* NOT FOR BROWSER ONLY END */
@@ -85,7 +97,7 @@ const Parser = {
85
97
  if (!path_1.default.isAbsolute(this.config)) {
86
98
  for (const p of this.configPaths) {
87
99
  try {
88
- this.config = require(path_1.default.resolve(process.cwd(), p, this.config));
100
+ this.config = jsonRequire(path_1.default.resolve(process.cwd(), p, this.config));
89
101
  break;
90
102
  }
91
103
  catch { }
@@ -29,11 +29,10 @@ declare interface Texvcjs {
29
29
  };
30
30
  }
31
31
  export declare const loadTexvcjs: () => Texvcjs | null;
32
- export declare const jsonTags: string[];
33
32
  export declare const loadJsonLSP: () => JSONLanguageService | null;
34
33
  export declare const loadCssLSP: () => CSSLanguageService | null;
35
34
  export declare const loadHtmlData: () => IHTMLDataProvider | null;
36
- export declare const loadStylelint: () => Promise<PublicApi | null>;
35
+ export declare const loadStylelint: () => PublicApi | null;
37
36
  /** embedded document */
38
37
  declare class EmbeddedDocument implements TextDocument {
39
38
  #private;
@@ -3,9 +3,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.EmbeddedCSSDocument = exports.EmbeddedJSONDocument = exports.loadStylelint = exports.loadHtmlData = exports.loadCssLSP = exports.loadJsonLSP = exports.jsonTags = exports.loadTexvcjs = void 0;
6
+ exports.EmbeddedCSSDocument = exports.EmbeddedJSONDocument = exports.loadStylelint = exports.loadHtmlData = exports.loadCssLSP = exports.loadJsonLSP = exports.loadTexvcjs = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const common_1 = require("@bhsd/common");
9
+ const constants_1 = require("../util/constants");
9
10
  let texcvjs;
10
11
  const loadTexvcjs = () => {
11
12
  NPM: {
@@ -22,7 +23,6 @@ const loadTexvcjs = () => {
22
23
  }
23
24
  };
24
25
  exports.loadTexvcjs = loadTexvcjs;
25
- exports.jsonTags = ['templatedata', 'mapframe', 'maplink'];
26
26
  let jsonLSP;
27
27
  const loadJsonLSP = () => {
28
28
  if (jsonLSP === undefined) {
@@ -38,10 +38,10 @@ const loadJsonLSP = () => {
38
38
  });
39
39
  const dir = path_1.default.join('..', '..', 'data', 'ext');
40
40
  jsonLSP.configure({
41
- schemas: exports.jsonTags.map((tag) => {
42
- const uri = path_1.default.join(dir, tag);
41
+ schemas: constants_1.jsonTags.map((tag) => {
42
+ const uri = path_1.default.join(dir, `${tag}.json`);
43
43
  try {
44
- const schema = require(tag === 'maplink' ? path_1.default.join(dir, 'mapframe') : uri);
44
+ const schema = require(tag === 'maplink' ? path_1.default.join(dir, 'mapframe.json') : uri);
45
45
  return {
46
46
  uri,
47
47
  fileMatch: [tag],
@@ -96,15 +96,15 @@ exports.loadHtmlData = loadHtmlData;
96
96
  let stylelint;
97
97
  const loadStylelint = () => {
98
98
  NPM: {
99
- stylelint ??= (async () => {
99
+ if (stylelint === undefined) {
100
100
  try {
101
- return (await import('stylelint')).default;
101
+ stylelint = require('stylelint');
102
102
  }
103
103
  catch /* c8 ignore start */ {
104
- return null;
104
+ stylelint = null;
105
105
  }
106
106
  /* c8 ignore stop */
107
- })();
107
+ }
108
108
  return stylelint;
109
109
  }
110
110
  };
@@ -236,14 +236,12 @@ const defaultLintRuleConfig = {
236
236
  ],
237
237
  'invalid-math': 2,
238
238
  };
239
- Object.freeze(defaultLintRuleConfig);
240
239
  const defaultLintConfig = {
241
240
  configurationComment: 'lint',
242
241
  ignoreDisables: false,
243
242
  fix: true,
244
243
  computeEditInfo: true,
245
244
  };
246
- Object.freeze(defaultLintConfig);
247
245
  /**
248
246
  * 验证错误级别是否符合规范
249
247
  * @param severity 错误级别
package/dist/lib/lsp.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import Parser from '../index';
2
+ import { Token } from '../src/index';
2
3
  import type { Range, Position, ColorInformation, ColorPresentation, FoldingRange, DocumentLink, Location, WorkspaceEdit, Diagnostic as DiagnosticBase, TextEdit, Hover, SignatureHelp, InlayHint, CodeAction, DocumentSymbol } from 'vscode-languageserver-types';
3
4
  import type { Config, LanguageService as LanguageServiceBase, CompletionItem, SignatureData } from '../base';
4
5
  import type { AttributeToken } from '../internal';
package/dist/lib/lsp.js CHANGED
@@ -22,6 +22,7 @@ const child_process_1 = require("child_process");
22
22
  const crypto_1 = require("crypto");
23
23
  const stylelint_util_1 = require("@bhsd/stylelint-util");
24
24
  const search_1 = __importDefault(require("../util/search"));
25
+ const constants_2 = require("../util/constants");
25
26
  const document_1 = require("./document");
26
27
  exports.tasks = new WeakMap();
27
28
  const refTags = new Set(['ref']), referencesTags = new Set(['ref', 'references']), nameAttrs = new Set(['name', 'follow']), groupAttrs = new Set(['group']), renameTypes = new Set([
@@ -279,7 +280,7 @@ const partialParse = async (wikitext, watch, include, config = index_1.default.g
279
280
  };
280
281
  /* NOT FOR BROWSER ONLY */
281
282
  /** @see https://www.npmjs.com/package/stylelint-config-recommended */
282
- const cssRules = { 'block-no-empty': null }, sources = { 'invalid-css': 'css', 'invalid-math': 'texvc' }, jsonSelector = document_1.jsonTags.map(s => `ext#${s}`).join(), scores = new Map();
283
+ const cssRules = { 'block-no-empty': null }, sources = { 'invalid-css': 'css', 'invalid-math': 'texvc' }, jsonSelector = constants_2.jsonTags.map(s => `ext#${s}`).join(), scores = new Map();
283
284
  let colors;
284
285
  /**
285
286
  * Correct the position of an error.
@@ -372,15 +373,15 @@ class LanguageService {
372
373
  exports.tasks.set(uri, this);
373
374
  /* NOT FOR BROWSER ONLY */
374
375
  const dataDir = path_1.default.join('..', '..', 'data'), extDir = path_1.default.join(dataDir, 'ext');
375
- this.#lilypondData = require(path_1.default.join(extDir, 'score'));
376
- this.#mathData = require(path_1.default.join(extDir, 'math'));
376
+ this.#lilypondData = require(path_1.default.join(extDir, 'score.json'));
377
+ this.#mathData = require(path_1.default.join(extDir, 'math.json'));
377
378
  /* NOT FOR BROWSER ONLY END */
378
379
  Object.defineProperties(this, {
379
380
  config: { enumerable: false },
380
381
  data: {
381
382
  enumerable: false,
382
383
  /* NOT FOR BROWSER ONLY */
383
- value: require(path_1.default.join(dataDir, 'signatures')),
384
+ value: require(path_1.default.join(dataDir, 'signatures.json')),
384
385
  },
385
386
  });
386
387
  }
@@ -491,15 +492,15 @@ class LanguageService {
491
492
  async provideDocumentColors(rgba, text, hsl = true) {
492
493
  const root = await this.#queue(text);
493
494
  /* NOT FOR BROWSER ONLY */
494
- colors ??= (async () => {
495
+ if (colors === undefined) {
495
496
  try {
496
- return new RegExp(String.raw `\b${Object.keys((await import('color-name')).default).join('|')}\b`, 'giu');
497
+ const { default: colorName } = require('color-name');
498
+ colors = new RegExp(String.raw `\b${Object.keys(colorName).join('|')}\b`, 'giu');
497
499
  }
498
500
  catch {
499
- return false;
501
+ colors = false;
500
502
  }
501
- })();
502
- const re = await colors;
503
+ }
503
504
  /* NOT FOR BROWSER ONLY END */
504
505
  return root.querySelectorAll('attr-value,parameter-value,arg-default').reverse().flatMap(token => {
505
506
  const { type, childNodes,
@@ -515,13 +516,13 @@ class LanguageService {
515
516
  /* NOT FOR BROWSER ONLY END */
516
517
  }
517
518
  /* NOT FOR BROWSER ONLY */
518
- const isStyle = re && type === 'attr-value' && parentNode.name === 'style';
519
+ const isStyle = colors && type === 'attr-value' && parentNode.name === 'style';
519
520
  /* NOT FOR BROWSER ONLY END */
520
521
  return childNodes.filter((child) => child.type === 'text').reverse().flatMap(child => {
521
522
  const { data } = child, parts = (0, common_1.splitColors)(data, hsl).filter(([, , , isColor]) => isColor);
522
523
  /* NOT FOR BROWSER ONLY */
523
524
  if (isStyle) {
524
- parts.push(...[...data.matchAll(re)].map(({ index, 0: s }) => [s, index, index + s.length, true]));
525
+ parts.push(...[...data.matchAll(colors)].map(({ index, 0: s }) => [s, index, index + s.length, true]));
525
526
  }
526
527
  /* NOT FOR BROWSER ONLY END */
527
528
  if (parts.length === 0) {
@@ -762,7 +763,7 @@ class LanguageService {
762
763
  },
763
764
  }));
764
765
  }
765
- else if (type === 'ext-inner' && document_1.jsonTags.includes(cur.name)) {
766
+ else if (type === 'ext-inner' && constants_2.jsonTags.includes(cur.name)) {
766
767
  const jsonLSP = (0, document_1.loadJsonLSP)();
767
768
  if (!jsonLSP) {
768
769
  return undefined;
@@ -822,7 +823,7 @@ class LanguageService {
822
823
  const root = await this.#queue(text), { lintConfig } = index_1.default, needFix = lintConfig.fix;
823
824
  lintConfig.fix = false;
824
825
  /* NOT FOR BROWSER ONLY */
825
- const stylelint = await (0, document_1.loadStylelint)(), jsonLSP = (0, document_1.loadJsonLSP)();
826
+ const stylelint = (0, document_1.loadStylelint)(), jsonLSP = (0, document_1.loadJsonLSP)();
826
827
  let s;
827
828
  NPM: if (jsonLSP) {
828
829
  s = lintConfig.rules['invalid-json'];
@@ -1314,7 +1315,7 @@ class LanguageService {
1314
1315
  const textDoc = new document_1.EmbeddedCSSDocument(root, offsetNode);
1315
1316
  return (0, document_1.loadCssLSP)().doHover(textDoc, position, textDoc.styleSheet) ?? undefined;
1316
1317
  }
1317
- else if (type === 'ext-inner' && document_1.jsonTags.includes(name)) {
1318
+ else if (type === 'ext-inner' && constants_2.jsonTags.includes(name)) {
1318
1319
  const jsonLSP = (0, document_1.loadJsonLSP)();
1319
1320
  if (!jsonLSP) {
1320
1321
  return undefined;
@@ -1434,8 +1435,12 @@ class LanguageService {
1434
1435
  return hints;
1435
1436
  }
1436
1437
  /** @private */
1438
+ querySelectorAll(selector) {
1439
+ return this.#done.querySelectorAll(selector);
1440
+ }
1441
+ /** @private */
1437
1442
  findStyleTokens() {
1438
- return this.#done.querySelectorAll(cssSelector).filter(({ lastChild }) => isAttr(lastChild));
1443
+ return this.querySelectorAll(cssSelector).filter(({ lastChild }) => isAttr(lastChild));
1439
1444
  }
1440
1445
  /**
1441
1446
  * Provide refactoring actions
@@ -1577,7 +1582,7 @@ class LanguageService {
1577
1582
  async setTargetWikipedia(wiki, user) {
1578
1583
  const [site, host] = index_1.default.getWMFSite(wiki);
1579
1584
  try {
1580
- const config = require(path_1.default.join('..', '..', 'config', site));
1585
+ const config = require(path_1.default.join('..', '..', 'config', `${site}.json`));
1581
1586
  this.config = index_1.default.getConfig(config);
1582
1587
  }
1583
1588
  catch {
@@ -1,4 +1,4 @@
1
- import type { LintError, AstNode as AstNodeBase, TokenTypes } from '../base';
1
+ import type { AstNode as AstNodeBase, TokenTypes, LintError } from '../base';
2
2
  import type { NodeLike } from '../mixin/nodeLike';
3
3
  import type { AstText, Token } from '../internal';
4
4
  export type AstNodes = AstText | Token;
package/dist/lib/node.js CHANGED
@@ -196,10 +196,13 @@ let AstNode = (() => {
196
196
  : 0;
197
197
  }
198
198
  return (0, lint_1.cache)(this.#rIndex[j], () => {
199
- const { childNodes } = this, n = j + (j < 0 ? childNodes.length : 0);
199
+ const { childNodes } = this, parentAIndex = this.#aIndex?.[0] === debug_1.Shadow.rev && this.#aIndex[1], n = j + (j < 0 ? childNodes.length : 0);
200
200
  let acc = this.getAttribute('padding');
201
201
  for (let i = 0; i < n; i++) {
202
202
  this.#rIndex[i] = [debug_1.Shadow.rev, acc];
203
+ if (parentAIndex !== false) {
204
+ childNodes[i].#aIndex = [debug_1.Shadow.rev, parentAIndex + acc];
205
+ }
203
206
  acc += childNodes[i].toString().length + this.getGaps(i);
204
207
  }
205
208
  return acc;
@@ -108,14 +108,18 @@ let ConverterFlagsToken = (() => {
108
108
  }
109
109
  /** @private */
110
110
  // eslint-disable-next-line @typescript-eslint/class-methods-use-this
111
- isInvalidFlag(flag, variant, unknown, valid) {
112
- return Boolean(flag) && !variant.has(flag) && !unknown.has(flag) && (variant.size > 0 || !valid.has(flag));
111
+ isInvalidFlag(flag, variant, unknown) {
112
+ return Boolean(flag)
113
+ && !variant.has(flag)
114
+ && !unknown.has(flag)
115
+ && (variant.size > 0 || !definedFlags.has(flag));
113
116
  }
114
117
  /** @private */
115
118
  lint(start = this.getAbsoluteIndex(), re) {
116
119
  LINT: {
117
- const variantFlags = this.getVariantFlags(), unknownFlags = this.getUnknownFlags(), validFlags = new Set(this.#flags.filter(flag => definedFlags.has(flag))), emptyFlagCount = this.#flags.filter(flag => !flag).length, knownFlagCount = this.#flags.length - unknownFlags.size - emptyFlagCount, { lintConfig } = index_1.default, { computeEditInfo, fix } = lintConfig, errors = super.lint(start, re);
118
- if (variantFlags.size === knownFlagCount || validFlags.size === knownFlagCount) {
120
+ const variantFlags = this.getVariantFlags(), unknownFlags = this.getUnknownFlags(), emptyFlagCount = this.#flags.filter(flag => !flag).length, knownFlagCount = this.#flags.length - unknownFlags.size - emptyFlagCount, { lintConfig } = index_1.default, { computeEditInfo, fix } = lintConfig, errors = super.lint(start, re);
121
+ if (variantFlags.size === knownFlagCount
122
+ || this.#flags.filter(flag => definedFlags.has(flag)).length === knownFlagCount) {
119
123
  return errors;
120
124
  }
121
125
  const rule = 'no-ignored', s = lintConfig.getSeverity(rule, 'conversionFlag');
@@ -123,7 +127,7 @@ let ConverterFlagsToken = (() => {
123
127
  const rect = new rect_1.BoundingRect(this, start);
124
128
  for (let i = 0; i < this.length; i++) {
125
129
  const child = this.childNodes[i], flag = child.text().trim();
126
- if (this.isInvalidFlag(flag, variantFlags, unknownFlags, validFlags)) {
130
+ if (this.isInvalidFlag(flag, variantFlags, unknownFlags)) {
127
131
  const e = (0, lint_1.generateForChild)(child, rect, rule, 'invalid-conversion-flag', s);
128
132
  if (computeEditInfo || fix) {
129
133
  if (variantFlags.size === 0 && definedFlags.has(flag.toUpperCase())) {
@@ -1,7 +1,7 @@
1
1
  import Parser from '../index';
2
2
  import { AstElement } from '../lib/element';
3
3
  import { AstText } from '../lib/text';
4
- import type { LintError, TokenTypes } from '../base';
4
+ import type { TokenTypes, LintError } from '../base';
5
5
  import type { Title, TitleOptions } from '../lib/title';
6
6
  import type { AstNodes } from '../internal';
7
7
  declare type ExtendedLintError = LintError[] & {
@@ -1,5 +1,5 @@
1
1
  import { NowikiBaseToken } from './base';
2
- import type { LintError, Config } from '../../base';
2
+ import type { Config, LintError } from '../../base';
3
3
  import type { Token } from '../../internal';
4
4
  /**
5
5
  * invisible HTML comment
@@ -5,12 +5,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.NowikiToken = void 0;
7
7
  const common_1 = require("@bhsd/common");
8
+ const constants_1 = require("../../util/constants");
8
9
  const lint_1 = require("../../util/lint");
9
10
  const rect_1 = require("../../lib/rect");
10
11
  const index_1 = __importDefault(require("../../index"));
11
12
  const base_1 = require("./base");
12
13
  /* NOT FOR BROWSER ONLY */
13
- const constants_1 = require("../../util/constants");
14
+ const constants_2 = require("../../util/constants");
14
15
  const document_1 = require("../../lib/document");
15
16
  /** @ignore */
16
17
  const updateLocation = ({ startIndex, startLine, startCol, endIndex, endLine, endCol }, { offset, line, column }, n) => {
@@ -56,22 +57,10 @@ class NowikiToken extends base_1.NowikiBaseToken {
56
57
  }
57
58
  NPM: {
58
59
  rule = 'invalid-json';
59
- const sSyntax = lintConfig.getSeverity(rule);
60
- /* NOT FOR BROWSER ONLY */
61
- const sDuplicate = lintConfig.getSeverity(rule, 'duplicate');
62
- /* NOT FOR BROWSER ONLY END */
63
- if (name === 'templatedata' && (sSyntax
64
- || sDuplicate)) {
65
- // browser版本使用`lintJSONNative()`
66
- return (0, common_1.lintJSON)(innerText).map(({ message, from, to = from, line, endLine = line, column, endColumn = column,
67
- /* NOT FOR BROWSER ONLY */
68
- severity, }) => {
69
- s =
70
- /* eslint-disable @stylistic/operator-linebreak */
71
- severity === 'warning' ?
72
- sDuplicate :
73
- /* eslint-enable @stylistic/operator-linebreak */
74
- sSyntax;
60
+ const sSyntax = lintConfig.getSeverity(rule), sDuplicate = lintConfig.getSeverity(rule, 'duplicate');
61
+ if (constants_1.jsonTags.includes(name) && (sSyntax || sDuplicate)) {
62
+ return (name === 'templatedata' ? common_1.lintJSON : common_1.lintJSONC)(innerText).map(({ message, from, to = from, line, endLine = line, column, endColumn = column, severity, }) => {
63
+ s = severity === 'warning' ? sDuplicate : sSyntax;
75
64
  if (!s) {
76
65
  return false;
77
66
  }
@@ -92,7 +81,7 @@ class NowikiToken extends base_1.NowikiBaseToken {
92
81
  /* NOT FOR BROWSER ONLY */
93
82
  rule = 'invalid-math';
94
83
  s = lintConfig.getSeverity(rule);
95
- if (s && constants_1.mathTags.has(name)) {
84
+ if (s && constants_2.mathTags.has(name)) {
96
85
  const texvcjs = (0, document_1.loadTexvcjs)();
97
86
  if (texvcjs) {
98
87
  const isChem = name !== 'math', display = previousSibling?.getAttr('display') ?? 'block';
@@ -1,6 +1,6 @@
1
1
  import { Token } from './index';
2
2
  import type { Config, LintError } from '../base';
3
- import type { AtomToken, SyntaxToken, TranscludeToken } from '../internal';
3
+ import type { AtomToken, TranscludeToken, SyntaxToken } from '../internal';
4
4
  /**
5
5
  * template or magic word parameter
6
6
  *
@@ -1,5 +1,5 @@
1
1
  import { TagPairToken } from './index';
2
- import type { LintError, Config } from '../../base';
2
+ import type { Config, LintError } from '../../base';
3
3
  import type { AstText, Token } from '../../internal';
4
4
  /**
5
5
  * `<includeonly>`, `<noinclude>` or `<onlyinclude>`
@@ -56,25 +56,13 @@ export declare abstract class TranscludeToken extends Token {
56
56
  * @param i position to be inserted at / 插入位置
57
57
  */
58
58
  insertAt<T extends ParameterToken>(token: T, i?: number): T;
59
- /**
60
- * Get all parameters
61
- *
62
- * 获取所有参数
63
- */
64
- getAllArgs(): ParameterToken[];
65
- /**
66
- * Get all anonymous parameters
67
- *
68
- * 获取所有匿名参数
69
- */
70
- getAnonArgs(): ParameterToken[];
71
59
  /**
72
60
  * Get parameters with the specified name
73
61
  *
74
62
  * 获取指定参数
75
63
  * @param key parameter name / 参数名
76
64
  */
77
- getArgs(key: string | number, exact?: boolean, copy?: boolean): Set<ParameterToken>;
65
+ getArgs(key: string | number, exact?: boolean, copy?: boolean, init?: boolean): Set<ParameterToken>;
78
66
  /**
79
67
  * Get duplicated parameters
80
68
  *
@@ -76,6 +76,7 @@ let TranscludeToken = (() => {
76
76
  #colon = ':';
77
77
  #raw = false;
78
78
  #args = new Map();
79
+ #anonCount = 0;
79
80
  #title;
80
81
  get type() {
81
82
  return this.#type;
@@ -317,19 +318,17 @@ let TranscludeToken = (() => {
317
318
  return errors;
318
319
  }
319
320
  }
320
- /**
321
- * 处理匿名参数更改
322
- * @param addedToken 新增的参数
323
- */
324
- #handleAnonArgChange(addedToken) {
325
- const args = this.getAnonArgs(), added = typeof addedToken !== 'number';
326
- for (let i = added ? args.indexOf(addedToken) : addedToken - 1; i < args.length; i++) {
327
- const token = args[i], { name } = token, newName = String(i + 1);
328
- if (name !== newName || token === addedToken) {
329
- token.setAttribute('name', newName);
330
- this.getArgs(newName, false, false).add(token);
331
- }
332
- }
321
+ #handleAnonArgChange(addedToken, append) {
322
+ /* eslint-disable @stylistic/operator-linebreak */
323
+ this.#anonCount +=
324
+ 1;
325
+ /* eslint-enable @stylistic/operator-linebreak */
326
+ let i;
327
+ // eslint-disable-next-line prefer-const
328
+ i = this.#anonCount - 1;
329
+ const token = addedToken, newName = String(i + 1);
330
+ token.setAttribute('name', newName);
331
+ this.getArgs(newName, false, false).add(token);
333
332
  }
334
333
  /**
335
334
  * @override
@@ -346,22 +345,6 @@ let TranscludeToken = (() => {
346
345
  }
347
346
  return token;
348
347
  }
349
- /**
350
- * Get all parameters
351
- *
352
- * 获取所有参数
353
- */
354
- getAllArgs() {
355
- return this.childNodes.filter((0, debug_1.isToken)('parameter'));
356
- }
357
- /**
358
- * Get all anonymous parameters
359
- *
360
- * 获取所有匿名参数
361
- */
362
- getAnonArgs() {
363
- return this.getAllArgs().filter(({ anon }) => anon);
364
- }
365
348
  // eslint-disable-next-line jsdoc/require-param
366
349
  /**
367
350
  * Get parameters with the specified name
@@ -369,7 +352,7 @@ let TranscludeToken = (() => {
369
352
  * 获取指定参数
370
353
  * @param key parameter name / 参数名
371
354
  */
372
- getArgs(key, exact, copy = true) {
355
+ getArgs(key, exact, copy = true, init = true) {
373
356
  const keyStr = String(key)
374
357
  .replace(/^[ \t\n\0\v]+|([^ \t\n\0\v])[ \t\n\0\v]+$/gu, '$1');
375
358
  let args;
@@ -377,7 +360,9 @@ let TranscludeToken = (() => {
377
360
  args = this.#args.get(keyStr);
378
361
  }
379
362
  else {
380
- args = new Set(this.getAllArgs().filter(({ name }) => keyStr === name));
363
+ /* eslint-disable @stylistic/function-paren-newline */
364
+ args = new Set();
365
+ /* eslint-enable @stylistic/function-paren-newline */
381
366
  this.#args.set(keyStr, args);
382
367
  }
383
368
  return args;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.mathTags = 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.BuildMethod = exports.MAX_STAGE = void 0;
4
4
  exports.MAX_STAGE = 11;
5
5
  var BuildMethod;
6
6
  (function (BuildMethod) {
@@ -13,5 +13,6 @@ exports.enMsg = (() => {
13
13
  })();
14
14
  exports.galleryParams = new Set(['alt', 'link', 'lang', 'page', 'caption']);
15
15
  exports.extensions = new Set(['tiff', 'tif', 'png', 'gif', 'jpg', 'jpeg', 'webp', 'xcf', 'pdf', 'svg', 'djvu']);
16
+ exports.jsonTags = ['templatedata', 'mapframe', 'maplink'];
16
17
  /* NOT FOR BROWSER ONLY */
17
18
  exports.mathTags = new Set(['math', 'chem', 'ce']);
package/dist/util/diff.js CHANGED
@@ -18,40 +18,42 @@ process.on('unhandledRejection', e => {
18
18
  * @param args shell输入参数
19
19
  */
20
20
  const cmd = (command, args) => new Promise(resolve => {
21
- let timer, shell;
22
- /**
23
- * 清除进程并返回
24
- * @param val 返回值
25
- */
26
- const r = (val) => {
27
- clearTimeout(timer);
28
- shell?.kill('SIGINT');
29
- resolve(val);
30
- };
31
- try {
32
- shell = (0, child_process_1.spawn)(command, args);
33
- timer = setTimeout(() => {
34
- /* c8 ignore next */
35
- shell.kill('SIGINT');
36
- }, 60 * 1e3);
37
- let buf = '';
38
- shell.stdout.on('data', data => {
39
- buf += String(data);
40
- });
41
- shell.stdout.on('end', () => {
42
- r(buf);
43
- });
44
- shell.on('exit', () => {
45
- r(shell.killed ? undefined : '');
46
- });
47
- shell.on('error', () => {
21
+ NPM: {
22
+ let timer, shell;
23
+ /**
24
+ * 清除进程并返回
25
+ * @param val 返回值
26
+ */
27
+ const r = (val) => {
28
+ clearTimeout(timer);
29
+ shell?.kill('SIGINT');
30
+ resolve(val);
31
+ };
32
+ try {
33
+ shell = (0, child_process_1.spawn)(command, args);
34
+ timer = setTimeout(() => {
35
+ /* c8 ignore next */
36
+ shell.kill('SIGINT');
37
+ }, 60 * 1e3);
38
+ let buf = '';
39
+ shell.stdout.on('data', data => {
40
+ buf += String(data);
41
+ });
42
+ shell.stdout.on('end', () => {
43
+ r(buf);
44
+ });
45
+ shell.on('exit', () => {
46
+ r(shell.killed ? undefined : '');
47
+ });
48
+ shell.on('error', () => {
49
+ r(undefined);
50
+ });
51
+ }
52
+ catch /* c8 ignore start */ {
48
53
  r(undefined);
49
- });
54
+ }
55
+ /* c8 ignore stop */
50
56
  }
51
- catch /* c8 ignore start */ {
52
- r(undefined);
53
- }
54
- /* c8 ignore stop */
55
57
  });
56
58
  exports.cmd = cmd;
57
59
  /* c8 ignore start */
@@ -62,37 +64,37 @@ exports.cmd = cmd;
62
64
  * @param uid 唯一标识
63
65
  */
64
66
  const diff = async (oldStr, newStr, uid) => {
65
- if (oldStr === newStr) {
66
- return;
67
+ NPM: {
68
+ if (oldStr === newStr) {
69
+ return;
70
+ }
71
+ const oldFile = `diffOld${uid}`, newFile = `diffNew${uid}`;
72
+ await Promise.all([promises_1.default.writeFile(oldFile, oldStr), promises_1.default.writeFile(newFile, newStr)]);
73
+ const stdout = await (0, exports.cmd)('git', [
74
+ 'diff',
75
+ '--color-words=[\xC0-\xFF][\x80-\xBF]+|<?/?\\w+/?>?|[^[:space:]]',
76
+ '-U0',
77
+ '--no-index',
78
+ oldFile,
79
+ newFile,
80
+ ]);
81
+ console.log(stdout?.split('\n').slice(4).join('\n'));
82
+ await Promise.allSettled([promises_1.default.unlink(oldFile), promises_1.default.unlink(newFile)]);
67
83
  }
68
- const oldFile = `diffOld${uid}`, newFile = `diffNew${uid}`;
69
- await Promise.all([promises_1.default.writeFile(oldFile, oldStr), promises_1.default.writeFile(newFile, newStr)]);
70
- const stdout = await (0, exports.cmd)('git', [
71
- 'diff',
72
- '--color-words=[\xC0-\xFF][\x80-\xBF]+|<?/?\\w+/?>?|[^[:space:]]',
73
- '-U0',
74
- '--no-index',
75
- oldFile,
76
- newFile,
77
- ]);
78
- console.log(stdout?.split('\n').slice(4).join('\n'));
79
- await Promise.allSettled([promises_1.default.unlink(oldFile), promises_1.default.unlink(newFile)]);
80
84
  };
81
85
  exports.diff = diff;
82
86
  /* c8 ignore stop */
83
87
  /* c8 ignore start */
84
88
  /** @implements */
85
89
  const error = (msg, ...args) => {
86
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
87
- console.error(util_1.default.styleText?.('red', msg) ?? msg, ...args);
90
+ console.error(util_1.default.styleText('red', msg), ...args);
88
91
  };
89
92
  exports.error = error;
90
93
  /* c8 ignore stop */
91
94
  /* c8 ignore start */
92
95
  /** @implements */
93
96
  const info = (msg, ...args) => {
94
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
95
- console.info(util_1.default.styleText?.('green', msg) ?? msg, ...args);
97
+ NPM: console.info(util_1.default.styleText('green', msg), ...args);
96
98
  };
97
99
  exports.info = info;
98
100
  /* c8 ignore stop */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikilint",
3
- "version": "2.35.2",
3
+ "version": "2.37.0",
4
4
  "description": "A Node.js linter for MediaWiki markup",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -12,7 +12,7 @@
12
12
  "bugs": {
13
13
  "url": "https://github.com/bhsd-harry/wikiparser-node/issues"
14
14
  },
15
- "license": "GPL-3.0",
15
+ "license": "GPL-3.0-or-later",
16
16
  "author": "Bhsd",
17
17
  "files": [
18
18
  "/errors/README",
@@ -53,6 +53,7 @@
53
53
  "coverage": "tsc --declaration false --target es2024 && npm run build:define && c8 npm run test:ci && node dist/script/coverage.js && open coverage/index.html",
54
54
  "test:unit": "mocha dist/test/test.js",
55
55
  "test:parser": "LC_ALL=en.UTF-8 mocha dist/test/parserTests.js",
56
+ "test:perf": "mocha --reporter spec dist/test/perf.js",
56
57
  "test": "npm run test:unit && npm run test:parser",
57
58
  "test:parseronly": "LSP=0 npm run test:parser",
58
59
  "test:ci": "LSP=0 npm test",
@@ -72,52 +73,51 @@
72
73
  ]
73
74
  },
74
75
  "dependencies": {
75
- "@bhsd/cm-util": "^0.1.0",
76
- "@bhsd/common": "^1.3.1",
76
+ "@bhsd/cm-util": "^1.0.0",
77
+ "@bhsd/common": "^2.0.0",
77
78
  "@bhsd/stylelint-util": "^1.0.1",
78
79
  "binary-search": "^1.3.6",
79
80
  "vscode-languageserver-types": "^3.17.5"
80
81
  },
81
82
  "optionalDependencies": {
82
83
  "color-name": "^2.0.0",
83
- "entities": "^7.0.1",
84
+ "entities": "^8.0.0",
84
85
  "mathoid-texvcjs": "^0.6.0",
85
- "minimatch": "^10.2.2",
86
- "stylelint": "^17.3.0",
87
- "vscode-css-languageservice": "^6.3.9",
88
- "vscode-html-languageservice": "^5.6.1",
89
- "vscode-json-languageservice": "^5.7.1"
86
+ "stylelint": "^17.5.0",
87
+ "vscode-css-languageservice": "^6.3.10",
88
+ "vscode-html-languageservice": "^5.6.2",
89
+ "vscode-json-languageservice": "^5.7.2"
90
90
  },
91
91
  "devDependencies": {
92
- "@bhsd/code-standard": "^2.0.1",
93
- "@bhsd/nodejs": "^0.1.0",
94
- "@bhsd/test-util": "^0.3.0",
95
- "@stylistic/eslint-plugin": "^5.8.0",
92
+ "@bhsd/code-standard": "^2.1.1",
93
+ "@bhsd/nodejs": "^1.0.0",
94
+ "@bhsd/test-util": "^0.4.0",
95
+ "@stylistic/eslint-plugin": "^5.10.0",
96
96
  "@types/color-name": "^2.0.0",
97
97
  "@types/color-rgba": "^2.1.3",
98
98
  "@types/mocha": "^10.0.10",
99
- "@types/node": "^24.10.13",
100
- "@typescript-eslint/eslint-plugin": "^8.54.0",
101
- "@typescript-eslint/parser": "^8.54.0",
102
- "c8": "^10.1.3",
99
+ "@types/node": "^24.11.0",
100
+ "@typescript-eslint/eslint-plugin": "^8.57.0",
101
+ "@typescript-eslint/parser": "^8.57.0",
102
+ "c8": "^11.0.0",
103
103
  "color-rgba": "^3.0.0",
104
104
  "diff2html-cli": "^5.2.15",
105
- "esbuild": "^0.27.3",
106
- "eslint": "^9.39.3",
105
+ "esbuild": "^0.27.4",
106
+ "eslint": "^9.39.4",
107
107
  "eslint-plugin-eslint-comments": "^3.2.0",
108
- "eslint-plugin-jsdoc": "^62.7.0",
109
- "eslint-plugin-jsonc": "^2.21.1",
110
- "eslint-plugin-n": "^17.23.2",
108
+ "eslint-plugin-jsdoc": "^62.7.1",
109
+ "eslint-plugin-jsonc": "^3.1.1",
110
+ "eslint-plugin-n": "^17.24.0",
111
111
  "eslint-plugin-promise": "^7.2.1",
112
- "eslint-plugin-regexp": "^3.0.0",
112
+ "eslint-plugin-regexp": "^3.1.0",
113
113
  "eslint-plugin-unicorn": "^63.0.0",
114
114
  "markdownlint-cli2": "^0.21.0",
115
115
  "mocha": "^11.7.5",
116
116
  "typescript": "^5.9.3",
117
- "v8r": "^5.1.0",
117
+ "v8r": "^6.0.0",
118
118
  "vscode-languageserver-textdocument": "^1.0.12"
119
119
  },
120
120
  "engines": {
121
- "node": ">=20.19.0"
121
+ "node": "^20.19.0 || ^22.13.0 || >=24.11.0"
122
122
  }
123
123
  }