wikilint 2.12.7 → 2.13.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/config/default.json +8 -2
- package/config/enwiki.json +8 -1
- package/config/zhwiki.json +14 -2
- package/dist/base.d.ts +28 -1
- package/dist/base.js +28 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -4
- package/dist/lib/element.js +4 -11
- package/dist/lib/title.js +1 -2
- package/dist/parser/selector.js +14 -0
- package/dist/src/attributes.js +15 -12
- package/dist/src/transclude.js +2 -2
- package/package.json +3 -3
package/config/default.json
CHANGED
|
@@ -704,6 +704,8 @@
|
|
|
704
704
|
"PAGELANGUAGE",
|
|
705
705
|
"=",
|
|
706
706
|
"#FORMAL",
|
|
707
|
+
"#bcp47",
|
|
708
|
+
"#dir",
|
|
707
709
|
"#timef",
|
|
708
710
|
"#timefl"
|
|
709
711
|
],
|
|
@@ -740,7 +742,9 @@
|
|
|
740
742
|
"静态重定向",
|
|
741
743
|
"靜態重新導向",
|
|
742
744
|
"NOGLOBAL",
|
|
743
|
-
"
|
|
745
|
+
"禁用全域用户页",
|
|
746
|
+
"EXPECTED_UNCONNECTED_PAGE",
|
|
747
|
+
"EXPECTUNUSEDTEMPLATE"
|
|
744
748
|
],
|
|
745
749
|
{
|
|
746
750
|
"forcetoc": "forcetoc",
|
|
@@ -768,7 +772,9 @@
|
|
|
768
772
|
"目录": "toc",
|
|
769
773
|
"目錄": "toc",
|
|
770
774
|
"archivedtalk": "archivedtalk",
|
|
771
|
-
"
|
|
775
|
+
"已存档讨论": "archivedtalk",
|
|
776
|
+
"notalk": "notalk",
|
|
777
|
+
"禁用讨论": "notalk"
|
|
772
778
|
}
|
|
773
779
|
],
|
|
774
780
|
"protocol": "bitcoin:|ftp://|ftps://|geo:|git://|gopher://|http://|https://|irc://|ircs://|magnet:|mailto:|matrix:|mms://|news:|nntp://|redis://|sftp://|sip:|sips:|sms:|ssh://|svn://|tel:|telnet://|urn:|worldwind://|xmpp:",
|
package/config/enwiki.json
CHANGED
|
@@ -120,6 +120,8 @@
|
|
|
120
120
|
"101": "Portal talk",
|
|
121
121
|
"118": "Draft",
|
|
122
122
|
"119": "Draft talk",
|
|
123
|
+
"126": "MOS",
|
|
124
|
+
"127": "MOS talk",
|
|
123
125
|
"710": "TimedText",
|
|
124
126
|
"711": "TimedText talk",
|
|
125
127
|
"828": "Module",
|
|
@@ -158,6 +160,8 @@
|
|
|
158
160
|
"portal talk": 101,
|
|
159
161
|
"draft": 118,
|
|
160
162
|
"draft talk": 119,
|
|
163
|
+
"mos": 126,
|
|
164
|
+
"mos talk": 127,
|
|
161
165
|
"timedtext": 710,
|
|
162
166
|
"timedtext talk": 711,
|
|
163
167
|
"module": 828,
|
|
@@ -333,6 +337,8 @@
|
|
|
333
337
|
"PAGELANGUAGE",
|
|
334
338
|
"=",
|
|
335
339
|
"#FORMAL",
|
|
340
|
+
"#bcp47",
|
|
341
|
+
"#dir",
|
|
336
342
|
"#timef",
|
|
337
343
|
"#timefl"
|
|
338
344
|
],
|
|
@@ -357,7 +363,8 @@
|
|
|
357
363
|
"NONEWSECTIONLINK",
|
|
358
364
|
"STATICREDIRECT",
|
|
359
365
|
"NOGLOBAL",
|
|
360
|
-
"EXPECTED_UNCONNECTED_PAGE"
|
|
366
|
+
"EXPECTED_UNCONNECTED_PAGE",
|
|
367
|
+
"EXPECTUNUSEDTEMPLATE"
|
|
361
368
|
],
|
|
362
369
|
{
|
|
363
370
|
"forcetoc": "forcetoc",
|
package/config/zhwiki.json
CHANGED
|
@@ -122,6 +122,8 @@
|
|
|
122
122
|
"103": "WikiProject talk",
|
|
123
123
|
"118": "Draft",
|
|
124
124
|
"119": "Draft talk",
|
|
125
|
+
"126": "MOS",
|
|
126
|
+
"127": "MOS talk",
|
|
125
127
|
"710": "TimedText",
|
|
126
128
|
"711": "TimedText talk",
|
|
127
129
|
"828": "Module",
|
|
@@ -260,6 +262,8 @@
|
|
|
260
262
|
"draft talk": 119,
|
|
261
263
|
"草稿讨论": 119,
|
|
262
264
|
"草稿討論": 119,
|
|
265
|
+
"mos": 126,
|
|
266
|
+
"mos talk": 127,
|
|
263
267
|
"timedtext": 710,
|
|
264
268
|
"timedtext talk": 711,
|
|
265
269
|
"module": 828,
|
|
@@ -646,6 +650,8 @@
|
|
|
646
650
|
"PAGELANGUAGE",
|
|
647
651
|
"=",
|
|
648
652
|
"#FORMAL",
|
|
653
|
+
"#bcp47",
|
|
654
|
+
"#dir",
|
|
649
655
|
"#timef",
|
|
650
656
|
"#timefl"
|
|
651
657
|
],
|
|
@@ -682,7 +688,9 @@
|
|
|
682
688
|
"静态重定向",
|
|
683
689
|
"靜態重新導向",
|
|
684
690
|
"NOGLOBAL",
|
|
685
|
-
"
|
|
691
|
+
"禁用全域用户页",
|
|
692
|
+
"EXPECTED_UNCONNECTED_PAGE",
|
|
693
|
+
"EXPECTUNUSEDTEMPLATE"
|
|
686
694
|
],
|
|
687
695
|
{
|
|
688
696
|
"forcetoc": "forcetoc",
|
|
@@ -708,7 +716,11 @@
|
|
|
708
716
|
"無目錄": "notoc",
|
|
709
717
|
"toc": "toc",
|
|
710
718
|
"目录": "toc",
|
|
711
|
-
"目錄": "toc"
|
|
719
|
+
"目錄": "toc",
|
|
720
|
+
"archivedtalk": "archivedtalk",
|
|
721
|
+
"已存档讨论": "archivedtalk",
|
|
722
|
+
"notalk": "notalk",
|
|
723
|
+
"禁用讨论": "notalk"
|
|
712
724
|
}
|
|
713
725
|
],
|
|
714
726
|
"protocol": "bitcoin:|ftp://|ftps://|geo:|git://|gopher://|http://|https://|irc://|ircs://|magnet:|mailto:|matrix:|mms://|news:|nntp://|redis://|sftp://|sip:|sips:|sms:|ssh://|svn://|tel:|telnet://|urn:|worldwind://|xmpp:",
|
package/dist/base.d.ts
CHANGED
|
@@ -12,6 +12,33 @@ export interface Config {
|
|
|
12
12
|
readonly excludes?: string[];
|
|
13
13
|
}
|
|
14
14
|
export type TokenTypes = 'root' | 'plain' | 'redirect' | 'redirect-syntax' | 'redirect-target' | 'onlyinclude' | 'noinclude' | 'include' | 'comment' | 'ext' | 'ext-attrs' | 'ext-attr-dirty' | 'ext-attr' | 'attr-key' | 'attr-value' | 'ext-inner' | 'arg' | 'arg-name' | 'arg-default' | 'hidden' | 'magic-word' | 'magic-word-name' | 'invoke-function' | 'invoke-module' | 'template' | 'template-name' | 'parameter' | 'parameter-key' | 'parameter-value' | 'heading' | 'heading-title' | 'heading-trail' | 'html' | 'html-attrs' | 'html-attr-dirty' | 'html-attr' | 'table' | 'tr' | 'td' | 'table-syntax' | 'table-attrs' | 'table-attr-dirty' | 'table-attr' | 'table-inter' | 'td-inner' | 'hr' | 'double-underscore' | 'link' | 'link-target' | 'link-text' | 'category' | 'file' | 'gallery-image' | 'imagemap-image' | 'image-parameter' | 'quote' | 'ext-link' | 'ext-link-text' | 'ext-link-url' | 'free-ext-link' | 'magic-link' | 'list' | 'dd' | 'list-range' | 'converter' | 'converter-flags' | 'converter-flag' | 'converter-rule' | 'converter-rule-variant' | 'converter-rule-to' | 'converter-rule-from' | 'param-line' | 'imagemap-link';
|
|
15
|
+
export declare const stages: {
|
|
16
|
+
redirect: number;
|
|
17
|
+
onlyinclude: number;
|
|
18
|
+
noinclude: number;
|
|
19
|
+
include: number;
|
|
20
|
+
comment: number;
|
|
21
|
+
ext: number;
|
|
22
|
+
arg: number;
|
|
23
|
+
'magic-word': number;
|
|
24
|
+
template: number;
|
|
25
|
+
heading: number;
|
|
26
|
+
html: number;
|
|
27
|
+
table: number;
|
|
28
|
+
hr: number;
|
|
29
|
+
'double-underscore': number;
|
|
30
|
+
link: number;
|
|
31
|
+
category: number;
|
|
32
|
+
file: number;
|
|
33
|
+
quote: number;
|
|
34
|
+
'ext-link': number;
|
|
35
|
+
'free-ext-link': number;
|
|
36
|
+
'magic-link': number;
|
|
37
|
+
list: number;
|
|
38
|
+
dd: number;
|
|
39
|
+
converter: number;
|
|
40
|
+
};
|
|
41
|
+
export type Stage = keyof typeof stages;
|
|
15
42
|
export declare const rules: readonly ["bold-header", "format-leakage", "fostered-content", "h1", "illegal-attr", "insecure-style", "invalid-gallery", "invalid-imagemap", "invalid-invoke", "invalid-isbn", "lonely-apos", "lonely-bracket", "lonely-http", "nested-link", "no-arg", "no-duplicate", "no-ignored", "obsolete-attr", "obsolete-tag", "parsing-order", "pipe-like", "table-layout", "tag-like", "unbalanced-header", "unclosed-comment", "unclosed-quote", "unclosed-table", "unescaped", "unknown-page", "unmatched-tag", "unterminated-url", "url-encoding", "var-anchor", "void-ext"];
|
|
16
43
|
export declare namespace LintError {
|
|
17
44
|
type Severity = 'error' | 'warning';
|
|
@@ -63,6 +90,6 @@ export interface Parser {
|
|
|
63
90
|
* @param include 是否嵌入
|
|
64
91
|
* @param maxStage 最大解析层级
|
|
65
92
|
*/
|
|
66
|
-
parse(wikitext: string, include?: boolean, maxStage?: number, config?: Config): Token;
|
|
93
|
+
parse(wikitext: string, include?: boolean, maxStage?: number | Stage | Stage[], config?: Config): Token;
|
|
67
94
|
}
|
|
68
95
|
export {};
|
package/dist/base.js
CHANGED
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.rules = void 0;
|
|
3
|
+
exports.rules = exports.stages = void 0;
|
|
4
|
+
exports.stages = {
|
|
5
|
+
redirect: 1,
|
|
6
|
+
onlyinclude: 1,
|
|
7
|
+
noinclude: 1,
|
|
8
|
+
include: 1,
|
|
9
|
+
comment: 1,
|
|
10
|
+
ext: 1,
|
|
11
|
+
arg: 2,
|
|
12
|
+
'magic-word': 2,
|
|
13
|
+
template: 2,
|
|
14
|
+
heading: 2,
|
|
15
|
+
html: 3,
|
|
16
|
+
table: 4,
|
|
17
|
+
hr: 5,
|
|
18
|
+
'double-underscore': 5,
|
|
19
|
+
link: 6,
|
|
20
|
+
category: 6,
|
|
21
|
+
file: 6,
|
|
22
|
+
quote: 7,
|
|
23
|
+
'ext-link': 8,
|
|
24
|
+
'free-ext-link': 9,
|
|
25
|
+
'magic-link': 9,
|
|
26
|
+
list: 10,
|
|
27
|
+
dd: 10,
|
|
28
|
+
converter: 11,
|
|
29
|
+
};
|
|
30
|
+
Object.setPrototypeOf(exports.stages, null);
|
|
4
31
|
exports.rules = [
|
|
5
32
|
'bold-header',
|
|
6
33
|
'format-leakage',
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Config, LintError, TokenTypes, Parser as ParserBase } from './base';
|
|
1
|
+
import type { Config, LintError, TokenTypes, Parser as ParserBase, Stage } from './base';
|
|
2
2
|
import type { Title } from './lib/title';
|
|
3
3
|
import type { Token } from './internal';
|
|
4
4
|
declare interface Parser extends ParserBase {
|
|
@@ -10,7 +10,7 @@ declare interface Parser extends ParserBase {
|
|
|
10
10
|
* @param include 是否嵌入
|
|
11
11
|
*/
|
|
12
12
|
normalizeTitle(title: string, defaultNs?: number, include?: boolean, config?: Config): Title;
|
|
13
|
-
parse(wikitext: string, include?: boolean, maxStage?: number, config?: Config): Token;
|
|
13
|
+
parse(wikitext: string, include?: boolean, maxStage?: number | Stage | Stage[], config?: Config): Token;
|
|
14
14
|
}
|
|
15
15
|
declare const Parser: Parser;
|
|
16
16
|
// @ts-expect-error mixed export styles
|
package/dist/index.js
CHANGED
|
@@ -22,13 +22,13 @@ const Parser = {
|
|
|
22
22
|
getConfig() {
|
|
23
23
|
if (typeof this.config === 'string') {
|
|
24
24
|
this.config = rootRequire(this.config, 'config');
|
|
25
|
+
if (this.config.doubleUnderscore.length < 3) {
|
|
26
|
+
(0, diff_1.error)(`The schema (${path.resolve(__dirname, '..', 'config', '.schema.json')}) of parser configuration is updated.`);
|
|
27
|
+
}
|
|
25
28
|
return this.getConfig();
|
|
26
29
|
}
|
|
27
30
|
const { doubleUnderscore } = this.config;
|
|
28
|
-
if (doubleUnderscore.length ===
|
|
29
|
-
(0, diff_1.error)(`The schema (${path.resolve(__dirname, '..', 'config', '.schema.json')}) of parser configuration is updated.`);
|
|
30
|
-
}
|
|
31
|
-
else if (doubleUnderscore[0].length === 0) {
|
|
31
|
+
if (doubleUnderscore.length > 2 && doubleUnderscore[0].length === 0) {
|
|
32
32
|
doubleUnderscore[0] = Object.keys(doubleUnderscore[2]);
|
|
33
33
|
}
|
|
34
34
|
return {
|
|
@@ -78,6 +78,10 @@ const Parser = {
|
|
|
78
78
|
/** @implements */
|
|
79
79
|
parse(wikitext, include, maxStage = constants_1.MAX_STAGE, config = Parser.getConfig()) {
|
|
80
80
|
wikitext = (0, string_1.tidy)(wikitext);
|
|
81
|
+
if (typeof maxStage !== 'number') {
|
|
82
|
+
const types = Array.isArray(maxStage) ? maxStage : [maxStage];
|
|
83
|
+
maxStage = Math.max(...types.map(t => base_1.stages[t] || constants_1.MAX_STAGE));
|
|
84
|
+
}
|
|
81
85
|
const { Token } = require('./src/index');
|
|
82
86
|
const root = debug_1.Shadow.run(() => {
|
|
83
87
|
const token = new Token(wikitext, config);
|
package/dist/lib/element.js
CHANGED
|
@@ -3,15 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.AstElement = void 0;
|
|
4
4
|
const string_1 = require("../util/string");
|
|
5
5
|
const debug_1 = require("../util/debug");
|
|
6
|
+
const selector_1 = require("../parser/selector");
|
|
6
7
|
const node_1 = require("./node");
|
|
7
|
-
/**
|
|
8
|
-
* 将选择器转化为类型谓词
|
|
9
|
-
* @param selector 选择器
|
|
10
|
-
*/
|
|
11
|
-
const getCondition = (selector) => (({ type, name }) => selector.split(',').some(str => {
|
|
12
|
-
const [t, ...ns] = str.trim().split('#');
|
|
13
|
-
return (!t || t === type) && ns.every(n => n === name);
|
|
14
|
-
}));
|
|
15
8
|
/** 类似HTMLElement */
|
|
16
9
|
class AstElement extends node_1.AstNode {
|
|
17
10
|
/** 子节点总数 */
|
|
@@ -64,7 +57,7 @@ class AstElement extends node_1.AstNode {
|
|
|
64
57
|
* @param selector 选择器
|
|
65
58
|
*/
|
|
66
59
|
closest(selector) {
|
|
67
|
-
const condition = getCondition(selector);
|
|
60
|
+
const condition = (0, selector_1.getCondition)(selector, this);
|
|
68
61
|
let { parentNode } = this;
|
|
69
62
|
while (parentNode) {
|
|
70
63
|
if (condition(parentNode)) {
|
|
@@ -98,7 +91,7 @@ class AstElement extends node_1.AstNode {
|
|
|
98
91
|
* @param selector 选择器
|
|
99
92
|
*/
|
|
100
93
|
querySelector(selector) {
|
|
101
|
-
const condition = getCondition(selector);
|
|
94
|
+
const condition = (0, selector_1.getCondition)(selector, this);
|
|
102
95
|
return this.#getElementBy(condition);
|
|
103
96
|
}
|
|
104
97
|
/**
|
|
@@ -123,7 +116,7 @@ class AstElement extends node_1.AstNode {
|
|
|
123
116
|
* @param selector 选择器
|
|
124
117
|
*/
|
|
125
118
|
querySelectorAll(selector) {
|
|
126
|
-
const condition = getCondition(selector);
|
|
119
|
+
const condition = (0, selector_1.getCondition)(selector, this);
|
|
127
120
|
return this.#getElementsBy(condition);
|
|
128
121
|
}
|
|
129
122
|
/**
|
package/dist/lib/title.js
CHANGED
|
@@ -52,7 +52,6 @@ class Title {
|
|
|
52
52
|
*/
|
|
53
53
|
constructor(title, defaultNs, config, decode, selfLink) {
|
|
54
54
|
const subpage = title.trim().startsWith('../');
|
|
55
|
-
title = (0, string_1.decodeHtml)(title);
|
|
56
55
|
if (decode && title.includes('%')) {
|
|
57
56
|
try {
|
|
58
57
|
const encoded = /%(?!21|3[ce]|5[bd]|7[b-d])[\da-f]{2}/iu.test(title);
|
|
@@ -61,7 +60,7 @@ class Title {
|
|
|
61
60
|
}
|
|
62
61
|
catch { }
|
|
63
62
|
}
|
|
64
|
-
title = title.replace(/[_ ]+/gu, ' ').trim();
|
|
63
|
+
title = (0, string_1.decodeHtml)(title).replace(/[_ ]+/gu, ' ').trim();
|
|
65
64
|
if (subpage) {
|
|
66
65
|
this.#ns = 0;
|
|
67
66
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCondition = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* 将选择器转化为类型谓词
|
|
6
|
+
* @param selector 选择器
|
|
7
|
+
* @param scope 作用对象
|
|
8
|
+
* @param has `:has()`伪选择器
|
|
9
|
+
*/
|
|
10
|
+
const getCondition = (selector, scope, has) => (({ type, name }) => selector.split(',').some(str => {
|
|
11
|
+
const [t, ...ns] = str.trim().split('#');
|
|
12
|
+
return (!t || t === type) && ns.every(n => n === name);
|
|
13
|
+
}));
|
|
14
|
+
exports.getCondition = getCondition;
|
package/dist/src/attributes.js
CHANGED
|
@@ -104,18 +104,7 @@ class AttributesToken extends index_2.Token {
|
|
|
104
104
|
errors.push(e);
|
|
105
105
|
}
|
|
106
106
|
for (const attr of childNodes) {
|
|
107
|
-
if (attr instanceof
|
|
108
|
-
const e = (0, lint_1.generateForChild)(attr, rect, 'no-ignored', 'containing invalid attribute');
|
|
109
|
-
e.suggestions = [
|
|
110
|
-
{
|
|
111
|
-
desc: 'remove',
|
|
112
|
-
range: [e.startIndex, e.endIndex],
|
|
113
|
-
text: ' ',
|
|
114
|
-
},
|
|
115
|
-
];
|
|
116
|
-
errors.push(e);
|
|
117
|
-
}
|
|
118
|
-
else if (attr instanceof attribute_1.AttributeToken) {
|
|
107
|
+
if (attr instanceof attribute_1.AttributeToken) {
|
|
119
108
|
const { name } = attr;
|
|
120
109
|
if (attrs.has(name)) {
|
|
121
110
|
duplicated.add(name);
|
|
@@ -125,6 +114,20 @@ class AttributesToken extends index_2.Token {
|
|
|
125
114
|
attrs.set(name, [attr]);
|
|
126
115
|
}
|
|
127
116
|
}
|
|
117
|
+
else {
|
|
118
|
+
const str = attr.text().trim();
|
|
119
|
+
if (str) {
|
|
120
|
+
const e = (0, lint_1.generateForChild)(attr, rect, 'no-ignored', 'containing invalid attribute', /[\p{L}\d]/u.test(str) ? 'error' : 'warning');
|
|
121
|
+
e.suggestions = [
|
|
122
|
+
{
|
|
123
|
+
desc: 'remove',
|
|
124
|
+
range: [e.startIndex, e.endIndex],
|
|
125
|
+
text: ' ',
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
errors.push(e);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
128
131
|
}
|
|
129
132
|
if (duplicated.size > 0) {
|
|
130
133
|
for (const key of duplicated) {
|
package/dist/src/transclude.js
CHANGED
|
@@ -265,7 +265,7 @@ class TranscludeToken extends index_2.Token {
|
|
|
265
265
|
getPossibleValues() {
|
|
266
266
|
const { type, name, childNodes } = this;
|
|
267
267
|
if (type === 'template') {
|
|
268
|
-
throw new Error(
|
|
268
|
+
throw new Error('TranscludeToken.getPossibleValues method is only for specific magic words!');
|
|
269
269
|
}
|
|
270
270
|
let start;
|
|
271
271
|
switch (name) {
|
|
@@ -279,7 +279,7 @@ class TranscludeToken extends index_2.Token {
|
|
|
279
279
|
start = 3;
|
|
280
280
|
break;
|
|
281
281
|
default:
|
|
282
|
-
throw new Error(
|
|
282
|
+
throw new Error('TranscludeToken.getPossibleValues method is only for specific magic words!');
|
|
283
283
|
}
|
|
284
284
|
const queue = childNodes.slice(start, start + 2).map(({ childNodes: [, value] }) => value);
|
|
285
285
|
for (let i = 0; i < queue.length;) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wikilint",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0",
|
|
4
4
|
"description": "A Node.js linter for MediaWiki markup",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mediawiki",
|
|
@@ -51,11 +51,11 @@
|
|
|
51
51
|
"chalk": "^4.1.2"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@bhsd/common": "^0.
|
|
54
|
+
"@bhsd/common": "^0.1.1",
|
|
55
55
|
"@types/node": "^20.11.6",
|
|
56
56
|
"v8r": "^3.0.0"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
|
-
"node": "
|
|
59
|
+
"node": ">=20.9.0"
|
|
60
60
|
}
|
|
61
61
|
}
|