eslint-plugin-node-dependencies 1.2.0 → 2.0.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 +10 -66
- package/dist/chunk-C7Uep-_p.mjs +20 -0
- package/dist/index.d.mts +28 -0
- package/dist/index.mjs +1633 -0
- package/dist/worker.d.mts +7 -0
- package/dist/worker.mjs +30 -0
- package/package.json +17 -10
- package/dist/configs/flat/recommended.d.ts +0 -17
- package/dist/configs/flat/recommended.js +0 -57
- package/dist/configs/recommended.d.ts +0 -9
- package/dist/configs/recommended.js +0 -15
- package/dist/configs/rules/recommended.d.ts +0 -5
- package/dist/configs/rules/recommended.js +0 -9
- package/dist/index.d.ts +0 -34
- package/dist/index.js +0 -54
- package/dist/meta.d.ts +0 -1
- package/dist/meta.js +0 -5
- package/dist/rules/absolute-version.d.ts +0 -2
- package/dist/rules/absolute-version.js +0 -160
- package/dist/rules/compat-engines.d.ts +0 -2
- package/dist/rules/compat-engines.js +0 -262
- package/dist/rules/no-deprecated.d.ts +0 -2
- package/dist/rules/no-deprecated.js +0 -62
- package/dist/rules/no-dupe-deps.d.ts +0 -2
- package/dist/rules/no-dupe-deps.js +0 -89
- package/dist/rules/no-restricted-deps.d.ts +0 -2
- package/dist/rules/no-restricted-deps.js +0 -258
- package/dist/rules/prefer-caret-range-version.d.ts +0 -2
- package/dist/rules/prefer-caret-range-version.js +0 -98
- package/dist/rules/prefer-tilde-range-version.d.ts +0 -2
- package/dist/rules/prefer-tilde-range-version.js +0 -99
- package/dist/rules/require-provenance-deps.d.ts +0 -2
- package/dist/rules/require-provenance-deps.js +0 -97
- package/dist/rules/valid-engines.d.ts +0 -2
- package/dist/rules/valid-engines.js +0 -11
- package/dist/rules/valid-semver.d.ts +0 -2
- package/dist/rules/valid-semver.js +0 -77
- package/dist/types.d.ts +0 -51
- package/dist/types.js +0 -2
- package/dist/utils/ast-utils.d.ts +0 -3
- package/dist/utils/ast-utils.js +0 -24
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/index.js +0 -72
- package/dist/utils/meta.d.ts +0 -30
- package/dist/utils/meta.js +0 -274
- package/dist/utils/package-json/index.d.ts +0 -1
- package/dist/utils/package-json/index.js +0 -17
- package/dist/utils/package-json/worker.d.mts +0 -1
- package/dist/utils/package-json/worker.mjs +0 -26
- package/dist/utils/regexp.d.ts +0 -3
- package/dist/utils/regexp.js +0 -20
- package/dist/utils/rules.d.ts +0 -2
- package/dist/utils/rules.js +0 -28
- package/dist/utils/semver-range.d.ts +0 -7
- package/dist/utils/semver-range.js +0 -37
- package/dist/utils/semver.d.ts +0 -7
- package/dist/utils/semver.js +0 -204
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1633 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-C7Uep-_p.mjs";
|
|
2
|
+
import Module, { createRequire } from "node:module";
|
|
3
|
+
import * as jsonParser from "jsonc-eslint-parser";
|
|
4
|
+
import { getStaticJSONValue } from "jsonc-eslint-parser";
|
|
5
|
+
import semver, { Range, SemVer, inc, intersects, lt, satisfies, subset } from "semver";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import npa from "npm-package-arg";
|
|
9
|
+
import { createSyncFn } from "synckit";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
12
|
+
//#region lib/utils/ast-utils.ts
|
|
13
|
+
/**
|
|
14
|
+
* Get the key from given object property node
|
|
15
|
+
*/
|
|
16
|
+
function getKeyFromJSONProperty(node) {
|
|
17
|
+
if (node.key.type === "JSONIdentifier") return node.key.name;
|
|
18
|
+
return node.key.value;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get the key from given object node
|
|
22
|
+
*/
|
|
23
|
+
function getKey(node) {
|
|
24
|
+
let parent = node.parent;
|
|
25
|
+
while (parent.type === "JSONUnaryExpression" || parent.type === "JSONBinaryExpression") parent = parent.parent;
|
|
26
|
+
if (parent.type === "JSONExpressionStatement") return null;
|
|
27
|
+
if (parent.type === "JSONArrayExpression") return parent.elements.indexOf(node);
|
|
28
|
+
return getKeyFromJSONProperty(parent);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region lib/utils/index.ts
|
|
33
|
+
/**
|
|
34
|
+
* Define the rule.
|
|
35
|
+
* @param ruleName ruleName
|
|
36
|
+
* @param rule rule module
|
|
37
|
+
*/
|
|
38
|
+
function createRule(ruleName, rule) {
|
|
39
|
+
return {
|
|
40
|
+
meta: {
|
|
41
|
+
...rule.meta,
|
|
42
|
+
docs: {
|
|
43
|
+
...rule.meta.docs,
|
|
44
|
+
url: `https://ota-meshi.github.io/eslint-plugin-node-dependencies/rules/${ruleName}.html`,
|
|
45
|
+
ruleId: `node-dependencies/${ruleName}`,
|
|
46
|
+
ruleName
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
create: rule.create
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Define the JSON visitor rule.
|
|
54
|
+
*/
|
|
55
|
+
function defineJsonVisitor(visitor) {
|
|
56
|
+
let stack = null;
|
|
57
|
+
const visitors = [];
|
|
58
|
+
for (const [selector, visit] of Object.entries(visitor)) if (visit) for (const sel of selector.split(",").map((s) => s.trim())) visitors.push({
|
|
59
|
+
test: (s) => {
|
|
60
|
+
if (!s.key || !s.upper || s.upper.key != null) return false;
|
|
61
|
+
return s.key === sel;
|
|
62
|
+
},
|
|
63
|
+
visit
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
"JSONObjectExpression, JSONArrayExpression"(node) {
|
|
67
|
+
stack = {
|
|
68
|
+
node,
|
|
69
|
+
upper: stack,
|
|
70
|
+
key: getKey(node)
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
"JSONObjectExpression, JSONArrayExpression:exit"() {
|
|
74
|
+
stack = stack && stack.upper;
|
|
75
|
+
},
|
|
76
|
+
JSONProperty(node) {
|
|
77
|
+
if (!stack) return;
|
|
78
|
+
for (const v of visitors) if (v.test(stack)) v.visit(node);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Composite all given visitors.
|
|
84
|
+
*/
|
|
85
|
+
function compositingVisitors(visitor, ...visitors) {
|
|
86
|
+
for (const v of visitors) for (const key in v) {
|
|
87
|
+
const orig = visitor[key];
|
|
88
|
+
if (orig) visitor[key] = (...args) => {
|
|
89
|
+
orig(...args);
|
|
90
|
+
v[key](...args);
|
|
91
|
+
};
|
|
92
|
+
else visitor[key] = v[key];
|
|
93
|
+
}
|
|
94
|
+
return visitor;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region lib/utils/regexp.ts
|
|
99
|
+
/**
|
|
100
|
+
* Convert to regexp
|
|
101
|
+
*/
|
|
102
|
+
function toRegExp(str) {
|
|
103
|
+
const regexp = parseRegExp(str);
|
|
104
|
+
if (regexp) return regexp;
|
|
105
|
+
return { test: (s) => s === str };
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Parse regexp string
|
|
109
|
+
*/
|
|
110
|
+
function parseRegExp(str) {
|
|
111
|
+
if (!str.startsWith("/")) return null;
|
|
112
|
+
const lastSlashIndex = str.lastIndexOf("/");
|
|
113
|
+
if (lastSlashIndex <= 1) return null;
|
|
114
|
+
return new RegExp(str.slice(1, lastSlashIndex), str.slice(lastSlashIndex + 1));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region lib/utils/semver.ts
|
|
119
|
+
/** Get the semver range instance from given value */
|
|
120
|
+
function getSemverRange(value) {
|
|
121
|
+
if (value == null) return null;
|
|
122
|
+
try {
|
|
123
|
+
return new Range(value);
|
|
124
|
+
} catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/** Normalize version */
|
|
129
|
+
function normalizeVer(ver) {
|
|
130
|
+
const n = normalizeSemverRange(ver);
|
|
131
|
+
if (n) return n.raw;
|
|
132
|
+
return ver.raw;
|
|
133
|
+
}
|
|
134
|
+
/** Normalize semver ranges. */
|
|
135
|
+
function normalizeSemverRange(...values) {
|
|
136
|
+
const map = /* @__PURE__ */ new Map();
|
|
137
|
+
for (const ver of values) for (const comparators of ver.set) {
|
|
138
|
+
const normalized = normalizeComparators(comparators);
|
|
139
|
+
if (map.has(normalized)) continue;
|
|
140
|
+
const normalizedVer = getSemverRange(normalized);
|
|
141
|
+
if (!normalizedVer) continue;
|
|
142
|
+
let consume = false;
|
|
143
|
+
let target = {
|
|
144
|
+
range: normalizedVer,
|
|
145
|
+
comparators
|
|
146
|
+
};
|
|
147
|
+
for (const [k, data] of map) {
|
|
148
|
+
if (subset(target.range, data.range)) {
|
|
149
|
+
consume = true;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
if (subset(data.range, target.range)) map.delete(k);
|
|
153
|
+
if (intersects(target.range, data.range)) {
|
|
154
|
+
const newComparators = joinComparators(comparators, data.comparators);
|
|
155
|
+
if (newComparators) {
|
|
156
|
+
target = {
|
|
157
|
+
range: new Range(normalizeComparators(newComparators)),
|
|
158
|
+
comparators: newComparators
|
|
159
|
+
};
|
|
160
|
+
map.delete(k);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (consume) continue;
|
|
165
|
+
map.set(target.range.raw, target);
|
|
166
|
+
}
|
|
167
|
+
return getSemverRange([...map].sort(([, a], [, b]) => {
|
|
168
|
+
const aVer = getMinVer(a.comparators);
|
|
169
|
+
const bVer = getMinVer(b.comparators);
|
|
170
|
+
return aVer.compare(bVer);
|
|
171
|
+
}).map(([v]) => v).join("||"));
|
|
172
|
+
/** Get min version */
|
|
173
|
+
function getMinVer(comparators) {
|
|
174
|
+
let min = null;
|
|
175
|
+
for (const comp of comparators) if (!min || comp.semver.compare(min) < 0) min = comp.semver;
|
|
176
|
+
return min;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/** Normalize comparators */
|
|
180
|
+
function normalizeComparators(comparators) {
|
|
181
|
+
const rangeComparator = toRangeComparator(comparators);
|
|
182
|
+
if (rangeComparator && rangeComparator.min && rangeComparator.max) {
|
|
183
|
+
if (rangeComparator.min.operator === ">=" && rangeComparator.max.operator === "<") {
|
|
184
|
+
if (rangeComparator.min.semver.major !== 0 && inc(rangeComparator.min.semver.version, "premajor") === rangeComparator.max.semver.version) return `^${rangeComparator.min.semver.version}`;
|
|
185
|
+
if (inc(rangeComparator.min.semver.version, "preminor") === rangeComparator.max.semver.version) return `~${rangeComparator.min.semver.version}`;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return comparators.map(normalizeComparator).join(" ");
|
|
189
|
+
}
|
|
190
|
+
/** Normalize comparator */
|
|
191
|
+
function normalizeComparator(comparator) {
|
|
192
|
+
if (comparator.operator === "") return comparator.value || "*";
|
|
193
|
+
return comparator.value;
|
|
194
|
+
}
|
|
195
|
+
/** Join */
|
|
196
|
+
function joinComparators(a, b) {
|
|
197
|
+
const aRangeComparator = toRangeComparator(a);
|
|
198
|
+
const bRangeComparator = toRangeComparator(b);
|
|
199
|
+
if (aRangeComparator && bRangeComparator) {
|
|
200
|
+
const comparators = [];
|
|
201
|
+
if (aRangeComparator.min && bRangeComparator.min) comparators.push(aRangeComparator.min.semver.compare(bRangeComparator.min.semver) <= 0 ? aRangeComparator.min : bRangeComparator.min);
|
|
202
|
+
if (aRangeComparator.max && bRangeComparator.max) comparators.push(aRangeComparator.max.semver.compare(bRangeComparator.max.semver) >= 0 ? aRangeComparator.max : bRangeComparator.max);
|
|
203
|
+
if (comparators.length === 0) return new Range("*").set[0];
|
|
204
|
+
return comparators;
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
/** Convert to RangeComparator */
|
|
209
|
+
function toRangeComparator(comparators) {
|
|
210
|
+
if (comparators.length === 2) {
|
|
211
|
+
if (comparators[0].operator === ">" || comparators[0].operator === ">=") {
|
|
212
|
+
if (comparators[1].operator === "<" || comparators[1].operator === "<=") return {
|
|
213
|
+
min: comparators[0],
|
|
214
|
+
max: comparators[1]
|
|
215
|
+
};
|
|
216
|
+
} else if (comparators[0].operator === "<" || comparators[0].operator === "<=") {
|
|
217
|
+
if (comparators[1].operator === ">" || comparators[1].operator === ">=") return {
|
|
218
|
+
min: comparators[1],
|
|
219
|
+
max: comparators[0]
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (comparators.length === 1) {
|
|
224
|
+
if (comparators[0].operator === ">" || comparators[0].operator === ">=") return {
|
|
225
|
+
min: comparators[0],
|
|
226
|
+
max: null
|
|
227
|
+
};
|
|
228
|
+
else if (comparators[0].operator === "<" || comparators[0].operator === "<=") return {
|
|
229
|
+
min: null,
|
|
230
|
+
max: comparators[0]
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
/** Get max version */
|
|
236
|
+
function maxNextVersion(range) {
|
|
237
|
+
let maxVer = null;
|
|
238
|
+
for (const comparators of range.set) {
|
|
239
|
+
let max = null;
|
|
240
|
+
let hasMin = false;
|
|
241
|
+
for (const comparator of comparators) {
|
|
242
|
+
if (isAnyComparator(comparator)) return null;
|
|
243
|
+
const compVer = new SemVer(comparator.semver.version);
|
|
244
|
+
if (comparator.operator === "<=" || comparator.operator === "<" || comparator.operator === "") {
|
|
245
|
+
if (comparator.operator === "<=") compVer.inc("prerelease");
|
|
246
|
+
if (!max || lt(max, compVer)) max = compVer;
|
|
247
|
+
} else if (comparator.operator === ">=" || comparator.operator === ">") hasMin = true;
|
|
248
|
+
}
|
|
249
|
+
if (max) {
|
|
250
|
+
if (!maxVer || lt(maxVer, max)) maxVer = max;
|
|
251
|
+
} else if (hasMin) return null;
|
|
252
|
+
}
|
|
253
|
+
return maxVer;
|
|
254
|
+
}
|
|
255
|
+
/** Checks whether the given comparator is ANY comparator or not. */
|
|
256
|
+
function isAnyComparator(comparator) {
|
|
257
|
+
return !comparator.semver.version;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
//#endregion
|
|
261
|
+
//#region lib/rules/absolute-version.ts
|
|
262
|
+
const PREFERS = [
|
|
263
|
+
"always",
|
|
264
|
+
"never",
|
|
265
|
+
"ignore"
|
|
266
|
+
];
|
|
267
|
+
const SCHEMA_FOR_DEPS_PROPERTIES = {
|
|
268
|
+
dependencies: { enum: PREFERS },
|
|
269
|
+
peerDependencies: { enum: PREFERS },
|
|
270
|
+
optionalDependencies: { enum: PREFERS },
|
|
271
|
+
devDependencies: { enum: PREFERS }
|
|
272
|
+
};
|
|
273
|
+
const DEFAULT = {
|
|
274
|
+
dependencies: "ignore",
|
|
275
|
+
peerDependencies: "ignore",
|
|
276
|
+
optionalDependencies: "ignore",
|
|
277
|
+
devDependencies: "always"
|
|
278
|
+
};
|
|
279
|
+
/**
|
|
280
|
+
* Convert from string option to object option
|
|
281
|
+
*/
|
|
282
|
+
function stringToOption(option) {
|
|
283
|
+
return {
|
|
284
|
+
dependencies: option,
|
|
285
|
+
peerDependencies: option,
|
|
286
|
+
optionalDependencies: option,
|
|
287
|
+
devDependencies: option
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Convert from object option to object option
|
|
292
|
+
*/
|
|
293
|
+
function objectToOption(option, defaults) {
|
|
294
|
+
return {
|
|
295
|
+
dependencies: option.dependencies || defaults.dependencies,
|
|
296
|
+
peerDependencies: option.peerDependencies || defaults.peerDependencies,
|
|
297
|
+
optionalDependencies: option.optionalDependencies || defaults.optionalDependencies,
|
|
298
|
+
devDependencies: option.devDependencies || defaults.devDependencies
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Parse option
|
|
303
|
+
*/
|
|
304
|
+
function parseOption(option) {
|
|
305
|
+
if (!option) return () => DEFAULT;
|
|
306
|
+
if (typeof option === "string") {
|
|
307
|
+
const objectOption = stringToOption(option);
|
|
308
|
+
return () => objectOption;
|
|
309
|
+
}
|
|
310
|
+
const baseOption = objectToOption(option, DEFAULT);
|
|
311
|
+
if (!option.overridePackages) return () => baseOption;
|
|
312
|
+
const overridePackages = Object.entries(option.overridePackages).map(([packageName, opt]) => {
|
|
313
|
+
const regexp = toRegExp(packageName);
|
|
314
|
+
return {
|
|
315
|
+
test: (s) => regexp.test(s),
|
|
316
|
+
...typeof opt === "string" ? stringToOption(opt) : objectToOption(opt, baseOption)
|
|
317
|
+
};
|
|
318
|
+
});
|
|
319
|
+
return (name) => {
|
|
320
|
+
for (const overridePackage of overridePackages) if (overridePackage.test(name)) return overridePackage;
|
|
321
|
+
return baseOption;
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
var absolute_version_default = createRule("absolute-version", {
|
|
325
|
+
meta: {
|
|
326
|
+
docs: {
|
|
327
|
+
description: "require or disallow absolute version of dependency.",
|
|
328
|
+
category: "Best Practices",
|
|
329
|
+
recommended: false
|
|
330
|
+
},
|
|
331
|
+
schema: [{ oneOf: [{ enum: PREFERS.filter((p) => p !== "ignore") }, {
|
|
332
|
+
type: "object",
|
|
333
|
+
properties: {
|
|
334
|
+
...SCHEMA_FOR_DEPS_PROPERTIES,
|
|
335
|
+
overridePackages: {
|
|
336
|
+
type: "object",
|
|
337
|
+
patternProperties: { "^(?:\\S+)$": { oneOf: [{ enum: PREFERS }, {
|
|
338
|
+
type: "object",
|
|
339
|
+
properties: SCHEMA_FOR_DEPS_PROPERTIES,
|
|
340
|
+
additionalProperties: false
|
|
341
|
+
}] } },
|
|
342
|
+
minProperties: 1,
|
|
343
|
+
additionalProperties: false
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
additionalProperties: false
|
|
347
|
+
}] }],
|
|
348
|
+
messages: {},
|
|
349
|
+
type: "suggestion"
|
|
350
|
+
},
|
|
351
|
+
create(context) {
|
|
352
|
+
if (!context.sourceCode.parserServices.isJSON) return {};
|
|
353
|
+
const getOption = parseOption(context.options[0]);
|
|
354
|
+
/** Define dependency visitor */
|
|
355
|
+
function defineVisitor(visitName) {
|
|
356
|
+
return (node) => {
|
|
357
|
+
const ver = getStaticJSONValue(node.value);
|
|
358
|
+
if (typeof ver !== "string" || ver == null) return;
|
|
359
|
+
const option = getOption(String(getKeyFromJSONProperty(node)))[visitName];
|
|
360
|
+
const semver = getSemverRange(ver);
|
|
361
|
+
if (semver == null) return;
|
|
362
|
+
if (option === "always") {
|
|
363
|
+
if (isAbsoluteVersion(semver)) return;
|
|
364
|
+
context.report({
|
|
365
|
+
loc: node.value.loc,
|
|
366
|
+
message: "Use the absolute version instead."
|
|
367
|
+
});
|
|
368
|
+
} else if (option === "never") {
|
|
369
|
+
if (!isAbsoluteVersion(semver)) return;
|
|
370
|
+
context.report({
|
|
371
|
+
loc: node.value.loc,
|
|
372
|
+
message: "Do not use the absolute version."
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
return defineJsonVisitor({
|
|
378
|
+
dependencies: defineVisitor("dependencies"),
|
|
379
|
+
peerDependencies: defineVisitor("peerDependencies"),
|
|
380
|
+
optionalDependencies: defineVisitor("optionalDependencies"),
|
|
381
|
+
devDependencies: defineVisitor("devDependencies")
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
/** Checks whether the given semver is absolute version or not */
|
|
386
|
+
function isAbsoluteVersion(semver) {
|
|
387
|
+
for (const comparators of semver.set) for (const comparator of comparators) {
|
|
388
|
+
if (isAnyComparator(comparator)) return false;
|
|
389
|
+
if (comparator.operator !== "=" && comparator.operator !== "") return false;
|
|
390
|
+
}
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
//#endregion
|
|
395
|
+
//#region lib/utils/package-json/index.ts
|
|
396
|
+
const syncPackageJson = createSyncFn(getWorkerPath(), { timeout: 1e4 });
|
|
397
|
+
/**
|
|
398
|
+
* Get the worker module path
|
|
399
|
+
*/
|
|
400
|
+
function getWorkerPath() {
|
|
401
|
+
const ext = path.extname(fileURLToPath(import.meta.url));
|
|
402
|
+
try {
|
|
403
|
+
return fileURLToPath(import.meta.resolve(`./worker${ext}`));
|
|
404
|
+
} catch {}
|
|
405
|
+
return createRequire(import.meta.url).resolve(`./worker${ext}`);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
//#endregion
|
|
409
|
+
//#region lib/utils/meta.ts
|
|
410
|
+
const TTL = 1e3 * 60 * 60;
|
|
411
|
+
const CACHE_VERSION = 2;
|
|
412
|
+
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
413
|
+
const CACHED_META_ROOT = path.join(dirname, `../.cached_meta`);
|
|
414
|
+
/**
|
|
415
|
+
* Get the meta info from given module name
|
|
416
|
+
*/
|
|
417
|
+
function getMetaFromNodeModules(name, ver, options) {
|
|
418
|
+
try {
|
|
419
|
+
const ownerJsonPath = options.ownerPackageJsonPath || options.context.filename;
|
|
420
|
+
const relativeTo = path.join(ownerJsonPath && path.isAbsolute(ownerJsonPath) ? path.dirname(ownerJsonPath) : options.context.cwd, "__placeholder__.js");
|
|
421
|
+
const req = Module.createRequire(relativeTo);
|
|
422
|
+
const where = req.resolve(`${name}/package.json`);
|
|
423
|
+
const pkg = req(where);
|
|
424
|
+
if (maybeMeta(pkg)) {
|
|
425
|
+
const vr = getSemverRange(ver);
|
|
426
|
+
if (typeof pkg.version === "string" && (!vr || satisfies(pkg.version, vr))) {
|
|
427
|
+
pkg._where = where;
|
|
428
|
+
return pkg;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
} catch {}
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Get the npm meta info from given module name and version
|
|
436
|
+
*/
|
|
437
|
+
function getMetaFromNpm(name, ver) {
|
|
438
|
+
const trimmed = ver.trim();
|
|
439
|
+
if (trimmed.startsWith("npm:")) {
|
|
440
|
+
let parsed = null;
|
|
441
|
+
try {
|
|
442
|
+
parsed = npa(`${trimmed.slice(4).trim()}`);
|
|
443
|
+
} catch {}
|
|
444
|
+
if (parsed && (parsed.type === "range" || parsed.type === "version" || parsed.type === "tag")) return getMetaFromNameAndSpec(parsed.name, parsed.fetchSpec?.trim());
|
|
445
|
+
}
|
|
446
|
+
if (trimmed.includes("/") || trimmed.includes(":")) return {
|
|
447
|
+
cache: [],
|
|
448
|
+
get: () => []
|
|
449
|
+
};
|
|
450
|
+
return getMetaFromNameAndSpec(name, ver.trim());
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Get the meta info from npm registry with given package name and spec
|
|
454
|
+
*/
|
|
455
|
+
function getMetaFromNameAndSpec(name, verOrTag) {
|
|
456
|
+
const { cache, get } = getMetaFromName(name, path.join(CACHED_META_ROOT, `${name}.json`));
|
|
457
|
+
let isTargetVersion;
|
|
458
|
+
let hasUnknown = false;
|
|
459
|
+
const range = getSemverRange(verOrTag || "*");
|
|
460
|
+
if (range) isTargetVersion = (meta) => {
|
|
461
|
+
if (!meta.version) return true;
|
|
462
|
+
return range.test(meta.version);
|
|
463
|
+
};
|
|
464
|
+
else {
|
|
465
|
+
const parsed = npa.resolve(name, verOrTag);
|
|
466
|
+
if (parsed.type === "tag") isTargetVersion = (meta) => {
|
|
467
|
+
if (!meta.version) return true;
|
|
468
|
+
const v = meta["dist-tags"]?.[parsed.fetchSpec];
|
|
469
|
+
if (v == null) hasUnknown = true;
|
|
470
|
+
return v === meta.version;
|
|
471
|
+
};
|
|
472
|
+
else return {
|
|
473
|
+
cache: [],
|
|
474
|
+
get: () => null
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
if (cache) {
|
|
478
|
+
let alive = cache.alive;
|
|
479
|
+
if (!alive && range) {
|
|
480
|
+
const maxNext = maxNextVersion(range);
|
|
481
|
+
if (maxNext) alive = cache.data.meta.some((m) => m.version && maxNext.compare(m.version) <= 0);
|
|
482
|
+
}
|
|
483
|
+
const metaList = cache.data.meta.filter(isTargetVersion);
|
|
484
|
+
if (alive) return {
|
|
485
|
+
cache: metaList,
|
|
486
|
+
get: () => hasUnknown ? null : metaList
|
|
487
|
+
};
|
|
488
|
+
return {
|
|
489
|
+
cache: metaList,
|
|
490
|
+
get: () => {
|
|
491
|
+
const list = get()?.filter(isTargetVersion) ?? null;
|
|
492
|
+
return hasUnknown && !list?.length ? null : list;
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
return {
|
|
497
|
+
cache: [],
|
|
498
|
+
get: () => {
|
|
499
|
+
const list = get()?.filter(isTargetVersion) ?? null;
|
|
500
|
+
return hasUnknown && !list?.length ? null : list;
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Get the meta info from given package name
|
|
506
|
+
*/
|
|
507
|
+
function getMetaFromName(name, cachedFilePath) {
|
|
508
|
+
return {
|
|
509
|
+
cache: getCache(),
|
|
510
|
+
get: () => {
|
|
511
|
+
return getMetaFromNameWithoutCache(name, cachedFilePath);
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
/** Get from cache */
|
|
515
|
+
function getCache() {
|
|
516
|
+
makeDirs(path.dirname(cachedFilePath));
|
|
517
|
+
if (!fs.existsSync(cachedFilePath)) return null;
|
|
518
|
+
const data = JSON.parse(fs.readFileSync(cachedFilePath, "utf8"));
|
|
519
|
+
if (!data || data.meta == null) return null;
|
|
520
|
+
if (data.v !== CACHE_VERSION) return null;
|
|
521
|
+
return {
|
|
522
|
+
data,
|
|
523
|
+
alive: Boolean(typeof data.expired === "number" && data.expired >= Date.now() || typeof data.timestamp === "number" && data.timestamp + TTL >= Date.now() || data.meta.length === 1 && data.meta[0].deprecated)
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Get the meta info from given package name
|
|
529
|
+
*/
|
|
530
|
+
function getMetaFromNameWithoutCache(name, cachedFilePath) {
|
|
531
|
+
let meta = [];
|
|
532
|
+
try {
|
|
533
|
+
const allMeta = syncPackageJson(name);
|
|
534
|
+
meta = Object.values(allMeta.versions).map((vm) => {
|
|
535
|
+
return {
|
|
536
|
+
version: vm.version,
|
|
537
|
+
engines: vm.engines,
|
|
538
|
+
dependencies: vm.dependencies,
|
|
539
|
+
peerDependencies: vm.peerDependencies,
|
|
540
|
+
optionalDependencies: vm.optionalDependencies,
|
|
541
|
+
"dist-tags": allMeta["dist-tags"],
|
|
542
|
+
deprecated: vm.deprecated,
|
|
543
|
+
dist: vm.dist
|
|
544
|
+
};
|
|
545
|
+
});
|
|
546
|
+
} catch {
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
const timestamp = Date.now();
|
|
550
|
+
const content = {
|
|
551
|
+
v: CACHE_VERSION,
|
|
552
|
+
meta,
|
|
553
|
+
timestamp,
|
|
554
|
+
expired: timestamp + Math.floor(Math.random() * 1e3 * 60)
|
|
555
|
+
};
|
|
556
|
+
fs.writeFileSync(cachedFilePath, JSON.stringify(content));
|
|
557
|
+
return meta;
|
|
558
|
+
}
|
|
559
|
+
/** Get the engines from given package.json value */
|
|
560
|
+
function getEngines(meta) {
|
|
561
|
+
if (!maybeMeta(meta)) return /* @__PURE__ */ new Map();
|
|
562
|
+
return getStrMap(meta.engines);
|
|
563
|
+
}
|
|
564
|
+
/** Get the dependencies from given package.json value */
|
|
565
|
+
function getDependencies(meta, kind) {
|
|
566
|
+
if (!maybeMeta(meta)) return /* @__PURE__ */ new Map();
|
|
567
|
+
return getStrMap(meta[kind]);
|
|
568
|
+
}
|
|
569
|
+
/** Get the map from given value */
|
|
570
|
+
function getStrMap(maybeObject) {
|
|
571
|
+
const map = /* @__PURE__ */ new Map();
|
|
572
|
+
if (typeof maybeObject !== "object" || !maybeObject || Array.isArray(maybeObject)) return map;
|
|
573
|
+
for (const [key, val] of Object.entries(maybeObject)) if (val != null) map.set(key, `${val}`);
|
|
574
|
+
return map;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Checks whether the given object is package.json meta data
|
|
578
|
+
*/
|
|
579
|
+
function maybeMeta(json) {
|
|
580
|
+
if (typeof json !== "object" || !json || Array.isArray(json)) return false;
|
|
581
|
+
return true;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Make directories
|
|
585
|
+
*/
|
|
586
|
+
function makeDirs(dir) {
|
|
587
|
+
const dirs = [dir];
|
|
588
|
+
while (!fs.existsSync(dirs[0])) dirs.unshift(path.dirname(dirs[0]));
|
|
589
|
+
dirs.shift();
|
|
590
|
+
for (const d of dirs) fs.mkdirSync(d);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
//#endregion
|
|
594
|
+
//#region lib/rules/compat-engines.ts
|
|
595
|
+
var EnginesContext = class EnginesContext {
|
|
596
|
+
engines;
|
|
597
|
+
unprocessedEngines = /* @__PURE__ */ new Set();
|
|
598
|
+
invalidEngines = /* @__PURE__ */ new Map();
|
|
599
|
+
validEngines = /* @__PURE__ */ new Set();
|
|
600
|
+
validDependencies = /* @__PURE__ */ new Set();
|
|
601
|
+
constructor(engineNames, validDependencies = /* @__PURE__ */ new Set()) {
|
|
602
|
+
this.engines = new Set(engineNames);
|
|
603
|
+
this.unprocessedEngines = new Set(this.engines);
|
|
604
|
+
this.validDependencies = validDependencies;
|
|
605
|
+
}
|
|
606
|
+
nextContext() {
|
|
607
|
+
return new EnginesContext(this.unprocessedEngines, this.validDependencies);
|
|
608
|
+
}
|
|
609
|
+
markAsProcessed(module) {
|
|
610
|
+
this.unprocessedEngines.delete(module);
|
|
611
|
+
}
|
|
612
|
+
isAllProcessed() {
|
|
613
|
+
return this.unprocessedEngines.size === 0;
|
|
614
|
+
}
|
|
615
|
+
markAsValid(module) {
|
|
616
|
+
this.validEngines.add(module);
|
|
617
|
+
this.invalidEngines.delete(module);
|
|
618
|
+
}
|
|
619
|
+
addInvalid(module, allowedVer) {
|
|
620
|
+
if (this.validEngines.has(module)) return;
|
|
621
|
+
const vers = this.invalidEngines.get(module);
|
|
622
|
+
if (vers) vers.set(allowedVer.raw, allowedVer);
|
|
623
|
+
else this.invalidEngines.set(module, new Map([[allowedVer.raw, allowedVer]]));
|
|
624
|
+
}
|
|
625
|
+
hasInvalid() {
|
|
626
|
+
return this.invalidEngines.size > 0;
|
|
627
|
+
}
|
|
628
|
+
getInvalid() {
|
|
629
|
+
return this.invalidEngines;
|
|
630
|
+
}
|
|
631
|
+
markAsValidDependency(name, ver) {
|
|
632
|
+
this.validDependencies.add(`${name}@${ver}`);
|
|
633
|
+
}
|
|
634
|
+
isValidDependency(name, ver) {
|
|
635
|
+
return this.validDependencies.has(`${name}@${ver}`);
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
/**
|
|
639
|
+
* Build adjusted version range for self.
|
|
640
|
+
*/
|
|
641
|
+
function buildAdjustRangeForSelf(comparisonType, original) {
|
|
642
|
+
const adjustVers = [];
|
|
643
|
+
for (const cc of original.set) {
|
|
644
|
+
if (cc.length === 1) {
|
|
645
|
+
if (cc[0].operator === ">" || cc[0].operator === ">=") {
|
|
646
|
+
adjustVers.push(`${cc[0].value} <${semver.inc(cc[0].semver.version, "premajor")}`);
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
adjustVers.push(cc.map((c) => c.value).join(" "));
|
|
651
|
+
}
|
|
652
|
+
const range = new semver.Range(adjustVers.join("||"));
|
|
653
|
+
if (comparisonType === "normal") return range;
|
|
654
|
+
if (comparisonType === "major") return range;
|
|
655
|
+
throw new Error(`Illegal comparisonType: ${comparisonType}`);
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Build adjusted version range for dependencies.
|
|
659
|
+
*/
|
|
660
|
+
function buildAdjustRangeForDeps(comparisonType, original) {
|
|
661
|
+
if (comparisonType === "normal") return original;
|
|
662
|
+
if (comparisonType === "major") {
|
|
663
|
+
const majorVers = [];
|
|
664
|
+
for (const cc of original.set) majorVers.push(cc.map((c) => {
|
|
665
|
+
if (c.operator === ">" || c.operator === ">=") return `${c.operator}${c.semver.major}`;
|
|
666
|
+
return c.value;
|
|
667
|
+
}).join(" "));
|
|
668
|
+
return new semver.Range(majorVers.join("||"));
|
|
669
|
+
}
|
|
670
|
+
throw new Error(`Illegal comparisonType: ${comparisonType}`);
|
|
671
|
+
}
|
|
672
|
+
/** Extract dependencies */
|
|
673
|
+
function extractDependencies(metaList) {
|
|
674
|
+
const dependencies = /* @__PURE__ */ new Map();
|
|
675
|
+
for (const meta of metaList) for (const [m, v] of [...getDependencies(meta, "dependencies"), ...getDependencies(meta, "peerDependencies")]) {
|
|
676
|
+
const range = getSemverRange(v);
|
|
677
|
+
if (!range) continue;
|
|
678
|
+
const ranges = dependencies.get(m);
|
|
679
|
+
if (ranges) ranges.push(range);
|
|
680
|
+
else dependencies.set(m, [range]);
|
|
681
|
+
}
|
|
682
|
+
return [...dependencies].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
683
|
+
}
|
|
684
|
+
var compat_engines_default = createRule("compat-engines", {
|
|
685
|
+
meta: {
|
|
686
|
+
docs: {
|
|
687
|
+
description: "enforce the versions of the engines of the dependencies to be compatible.",
|
|
688
|
+
category: "Possible Errors",
|
|
689
|
+
recommended: true
|
|
690
|
+
},
|
|
691
|
+
schema: [{
|
|
692
|
+
type: "object",
|
|
693
|
+
properties: {
|
|
694
|
+
deep: { type: "boolean" },
|
|
695
|
+
comparisonType: { enum: ["normal", "major"] }
|
|
696
|
+
},
|
|
697
|
+
additionalProperties: false
|
|
698
|
+
}],
|
|
699
|
+
messages: {},
|
|
700
|
+
type: "problem"
|
|
701
|
+
},
|
|
702
|
+
create(context) {
|
|
703
|
+
if (!context.sourceCode.parserServices.isJSON) return {};
|
|
704
|
+
const deep = context.options[0]?.deep !== false;
|
|
705
|
+
const comparisonType = context.options[0]?.comparisonType ?? "normal";
|
|
706
|
+
const selfEngines = /* @__PURE__ */ new Map();
|
|
707
|
+
/**
|
|
708
|
+
* Process meta data
|
|
709
|
+
*/
|
|
710
|
+
function processMeta(ctx, meta) {
|
|
711
|
+
const depEngines = getEngines(meta);
|
|
712
|
+
for (const module of ctx.engines) {
|
|
713
|
+
const selfVer = selfEngines.get(module);
|
|
714
|
+
const engineValue = depEngines.get(module);
|
|
715
|
+
if (engineValue && engineValue !== "*") ctx.markAsProcessed(module);
|
|
716
|
+
const depVer = getSemverRange(engineValue || "*");
|
|
717
|
+
if (!depVer) continue;
|
|
718
|
+
if (semver.subset(selfVer.adjust, buildAdjustRangeForDeps(comparisonType, depVer))) ctx.markAsValid(module);
|
|
719
|
+
else ctx.addInvalid(module, depVer);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Process dependency module
|
|
724
|
+
*/
|
|
725
|
+
function processDependencyModule(ctx, name, ver, modules, node) {
|
|
726
|
+
const currModules = [...modules, `${name}@${ver}`];
|
|
727
|
+
const nodeModulesMeta = getMetaFromNodeModules(name, ver, { context });
|
|
728
|
+
if (nodeModulesMeta) {
|
|
729
|
+
processMeta(ctx, nodeModulesMeta);
|
|
730
|
+
if (!ctx.hasInvalid() && ctx.isAllProcessed()) return;
|
|
731
|
+
}
|
|
732
|
+
const metaData = getMetaFromNpm(name, ver);
|
|
733
|
+
for (const meta of metaData.cache) {
|
|
734
|
+
processMeta(ctx, meta);
|
|
735
|
+
if (!ctx.hasInvalid() && ctx.isAllProcessed()) return;
|
|
736
|
+
}
|
|
737
|
+
const metaList = metaData.get();
|
|
738
|
+
if (!metaList) return;
|
|
739
|
+
for (const meta of metaList) {
|
|
740
|
+
processMeta(ctx, meta);
|
|
741
|
+
if (!ctx.hasInvalid() && ctx.isAllProcessed()) return;
|
|
742
|
+
}
|
|
743
|
+
for (const [module, allowedVers] of ctx.getInvalid()) {
|
|
744
|
+
const selfVer = selfEngines.get(module);
|
|
745
|
+
const depVer = normalizeSemverRange(...allowedVers.values()) || [...allowedVers.values()].pop();
|
|
746
|
+
if (semver.subset(selfVer.adjust, buildAdjustRangeForDeps(comparisonType, depVer))) continue;
|
|
747
|
+
context.report({
|
|
748
|
+
loc: node.loc,
|
|
749
|
+
message: `${currModules.map((m) => `"${m}"`).join(" >> ")} is not compatible with "${module}@${normalizeVer(selfVer.original)}". Allowed is: "${module}@${normalizeVer(depVer)}"`
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
if (ctx.isAllProcessed()) return;
|
|
753
|
+
ctx.markAsValidDependency(name, ver);
|
|
754
|
+
if (deep) for (const [n, ranges] of extractDependencies(metaList)) {
|
|
755
|
+
const v = normalizeSemverRange(...ranges);
|
|
756
|
+
if (v && !ctx.isValidDependency(n, v.raw)) processDependencyModule(ctx.nextContext(), n, v.raw, currModules, node);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return compositingVisitors({ JSONExpressionStatement(node) {
|
|
760
|
+
const expr = node.expression;
|
|
761
|
+
if (expr.type !== "JSONObjectExpression") return;
|
|
762
|
+
const enginesNode = expr.properties.find((p) => getKeyFromJSONProperty(p) === "engines");
|
|
763
|
+
if (!enginesNode) return;
|
|
764
|
+
for (const [key, val] of getEngines({ engines: getStaticJSONValue(enginesNode.value) })) {
|
|
765
|
+
const selfVer = getSemverRange(val);
|
|
766
|
+
if (!selfVer) continue;
|
|
767
|
+
selfEngines.set(key, {
|
|
768
|
+
adjust: buildAdjustRangeForSelf(comparisonType, selfVer),
|
|
769
|
+
original: selfVer
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
} }, defineJsonVisitor({ "dependencies, peerDependencies"(node) {
|
|
773
|
+
if (selfEngines.size === 0) return;
|
|
774
|
+
const name = getKeyFromJSONProperty(node);
|
|
775
|
+
const ver = getStaticJSONValue(node.value);
|
|
776
|
+
if (typeof name !== "string" || typeof ver !== "string") return;
|
|
777
|
+
processDependencyModule(new EnginesContext(selfEngines.keys()), name, ver, [], node);
|
|
778
|
+
} }));
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
//#endregion
|
|
783
|
+
//#region lib/rules/no-deprecated.ts
|
|
784
|
+
var no_deprecated_default = createRule("no-deprecated", {
|
|
785
|
+
meta: {
|
|
786
|
+
docs: {
|
|
787
|
+
description: "disallow having dependencies on deprecate packages.",
|
|
788
|
+
category: "Best Practices",
|
|
789
|
+
recommended: false
|
|
790
|
+
},
|
|
791
|
+
schema: [{
|
|
792
|
+
type: "object",
|
|
793
|
+
properties: {
|
|
794
|
+
devDependencies: { type: "boolean" },
|
|
795
|
+
allows: {
|
|
796
|
+
type: "array",
|
|
797
|
+
items: { type: "string" },
|
|
798
|
+
uniqueItems: true
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
additionalProperties: false
|
|
802
|
+
}],
|
|
803
|
+
messages: {},
|
|
804
|
+
type: "suggestion"
|
|
805
|
+
},
|
|
806
|
+
create(context) {
|
|
807
|
+
if (!context.sourceCode.parserServices.isJSON) return {};
|
|
808
|
+
return defineJsonVisitor({ [Boolean(context.options[0]?.devDependencies) ? "dependencies, peerDependencies, devDependencies" : "dependencies, peerDependencies"](node) {
|
|
809
|
+
const name = getKeyFromJSONProperty(node);
|
|
810
|
+
const ver = getStaticJSONValue(node.value);
|
|
811
|
+
if (typeof name !== "string" || typeof ver !== "string" || !ver) return;
|
|
812
|
+
if (context.options[0]?.allows?.includes(name)) return;
|
|
813
|
+
const meta = getMetaFromNpm(name, ver).get();
|
|
814
|
+
const deprecated = meta && meta.length && meta[meta.length - 1].deprecated;
|
|
815
|
+
if (deprecated) context.report({
|
|
816
|
+
loc: node.loc,
|
|
817
|
+
message: `${deprecated}`
|
|
818
|
+
});
|
|
819
|
+
} });
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
//#endregion
|
|
824
|
+
//#region lib/rules/no-dupe-deps.ts
|
|
825
|
+
const DEPS$1 = [
|
|
826
|
+
"dependencies",
|
|
827
|
+
"peerDependencies",
|
|
828
|
+
"optionalDependencies",
|
|
829
|
+
"devDependencies"
|
|
830
|
+
];
|
|
831
|
+
var AllowDuplicates = class {
|
|
832
|
+
edges = [];
|
|
833
|
+
add(d1, d2) {
|
|
834
|
+
this.edges.push([d1, d2]);
|
|
835
|
+
}
|
|
836
|
+
isAllowedDuplicate(dep1, dep2) {
|
|
837
|
+
return this.edges.some(([d1, d2]) => d1 === dep1 && d2 === dep2 || d2 === dep1 && d1 === dep2);
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
var no_dupe_deps_default = createRule("no-dupe-deps", {
|
|
841
|
+
meta: {
|
|
842
|
+
docs: {
|
|
843
|
+
description: "disallow duplicate dependencies.",
|
|
844
|
+
category: "Possible Errors",
|
|
845
|
+
recommended: true
|
|
846
|
+
},
|
|
847
|
+
schema: [],
|
|
848
|
+
messages: { duplicated: "Duplicated dependency '{{name}}'." },
|
|
849
|
+
type: "problem"
|
|
850
|
+
},
|
|
851
|
+
create(context) {
|
|
852
|
+
if (!context.sourceCode.parserServices.isJSON) return {};
|
|
853
|
+
const allowDuplicates = new AllowDuplicates();
|
|
854
|
+
allowDuplicates.add("devDependencies", "peerDependencies");
|
|
855
|
+
allowDuplicates.add("devDependencies", "optionalDependencies");
|
|
856
|
+
const maps = {
|
|
857
|
+
dependencies: /* @__PURE__ */ new Map(),
|
|
858
|
+
peerDependencies: /* @__PURE__ */ new Map(),
|
|
859
|
+
optionalDependencies: /* @__PURE__ */ new Map(),
|
|
860
|
+
devDependencies: /* @__PURE__ */ new Map()
|
|
861
|
+
};
|
|
862
|
+
const reported = /* @__PURE__ */ new Set();
|
|
863
|
+
/** Report */
|
|
864
|
+
function report(name, node) {
|
|
865
|
+
if (reported.has(node)) return;
|
|
866
|
+
reported.add(node);
|
|
867
|
+
context.report({
|
|
868
|
+
loc: node.key.loc,
|
|
869
|
+
messageId: "duplicated",
|
|
870
|
+
data: { name }
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
/** Verify */
|
|
874
|
+
function verify(depsName, name, node) {
|
|
875
|
+
for (const dep of DEPS$1) {
|
|
876
|
+
if (allowDuplicates.isAllowedDuplicate(dep, depsName)) continue;
|
|
877
|
+
const dupeNode = maps[dep].get(name);
|
|
878
|
+
if (dupeNode) {
|
|
879
|
+
report(name, dupeNode);
|
|
880
|
+
report(name, node);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
/** Define dependency visitor */
|
|
885
|
+
function defineVisitor(depsName) {
|
|
886
|
+
return (node) => {
|
|
887
|
+
const name = String(getKeyFromJSONProperty(node));
|
|
888
|
+
verify(depsName, name, node);
|
|
889
|
+
maps[depsName].set(name, node);
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
return defineJsonVisitor({
|
|
893
|
+
dependencies: defineVisitor("dependencies"),
|
|
894
|
+
peerDependencies: defineVisitor("peerDependencies"),
|
|
895
|
+
optionalDependencies: defineVisitor("optionalDependencies"),
|
|
896
|
+
devDependencies: defineVisitor("devDependencies")
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
//#endregion
|
|
902
|
+
//#region lib/rules/no-restricted-deps.ts
|
|
903
|
+
const DEPS = [
|
|
904
|
+
"dependencies",
|
|
905
|
+
"peerDependencies",
|
|
906
|
+
"optionalDependencies"
|
|
907
|
+
];
|
|
908
|
+
var Deps = class {
|
|
909
|
+
map = Object.create(null);
|
|
910
|
+
list = [];
|
|
911
|
+
push(name, ver, ownerPackageJsonPath) {
|
|
912
|
+
const range = getSemverRange(ver);
|
|
913
|
+
if (range) {
|
|
914
|
+
const data = this.map[name];
|
|
915
|
+
if (data) {
|
|
916
|
+
const newRange = normalizeSemverRange(data.range, range);
|
|
917
|
+
this.map[name] = {
|
|
918
|
+
range: newRange,
|
|
919
|
+
ownerPackageJsonPath: ownerPackageJsonPath || data.ownerPackageJsonPath
|
|
920
|
+
};
|
|
921
|
+
} else this.map[name] = {
|
|
922
|
+
range,
|
|
923
|
+
ownerPackageJsonPath
|
|
924
|
+
};
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
this.list.push({
|
|
928
|
+
name,
|
|
929
|
+
ver,
|
|
930
|
+
ownerPackageJsonPath
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
pop() {
|
|
934
|
+
const resultForList = this.list.shift();
|
|
935
|
+
if (resultForList) return resultForList;
|
|
936
|
+
const [key] = Object.keys(this.map);
|
|
937
|
+
if (key) {
|
|
938
|
+
const data = this.map[key];
|
|
939
|
+
delete this.map[key];
|
|
940
|
+
return {
|
|
941
|
+
name: key,
|
|
942
|
+
ver: normalizeVer(data.range),
|
|
943
|
+
ownerPackageJsonPath: data.ownerPackageJsonPath
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
var DeepValidateContext = class {
|
|
950
|
+
deepValidatedCache = /* @__PURE__ */ new Map();
|
|
951
|
+
context;
|
|
952
|
+
validator;
|
|
953
|
+
constructor(context, validator) {
|
|
954
|
+
this.context = context;
|
|
955
|
+
this.validator = validator;
|
|
956
|
+
}
|
|
957
|
+
buildDeepValidator(deepOption) {
|
|
958
|
+
return (n, v) => this.deepDepsValidate(n, v, deepOption);
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Deep dependency validate
|
|
962
|
+
*/
|
|
963
|
+
deepDepsValidate(packageName, version, deepOption) {
|
|
964
|
+
const { validator: validate, context } = this;
|
|
965
|
+
const depsQueue = new Deps();
|
|
966
|
+
depsQueue.push(packageName, version);
|
|
967
|
+
let dep;
|
|
968
|
+
while (dep = depsQueue.pop()) {
|
|
969
|
+
const key = `${dep.name}@${dep.ver}`;
|
|
970
|
+
if (this.deepValidatedCache.has(key)) {
|
|
971
|
+
const result = this.deepValidatedCache.get(key);
|
|
972
|
+
if (result) return result;
|
|
973
|
+
} else {
|
|
974
|
+
const result = validateWithoutCache(dep.name, dep.ver, dep.ownerPackageJsonPath);
|
|
975
|
+
this.deepValidatedCache.set(key, result);
|
|
976
|
+
if (result) return result;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
return null;
|
|
980
|
+
/**
|
|
981
|
+
* Dependency validate
|
|
982
|
+
*/
|
|
983
|
+
function validateWithoutCache(name, ver, ownerPackageJsonPath) {
|
|
984
|
+
const result = validate(name, ver);
|
|
985
|
+
if (result) return result;
|
|
986
|
+
for (const { name: n, ver: v, packageJsonPath } of iterateDeps(name, ver, ownerPackageJsonPath)) {
|
|
987
|
+
const r = validate(n, v);
|
|
988
|
+
if (r) return r;
|
|
989
|
+
depsQueue.push(n, v, packageJsonPath);
|
|
990
|
+
}
|
|
991
|
+
return null;
|
|
992
|
+
}
|
|
993
|
+
/** Iterate deps */
|
|
994
|
+
function* iterateDeps(name, ver, ownerPackageJsonPath) {
|
|
995
|
+
yield* iterateDepsForMeta(getMetaFromNodeModules(name, ver, {
|
|
996
|
+
context,
|
|
997
|
+
ownerPackageJsonPath
|
|
998
|
+
}));
|
|
999
|
+
if (deepOption === "server") {
|
|
1000
|
+
const metaData = getMetaFromNpm(name, ver);
|
|
1001
|
+
for (const meta of metaData.cache) yield* iterateDepsForMeta(meta);
|
|
1002
|
+
const metaList = metaData.get();
|
|
1003
|
+
if (metaList) for (const meta of metaList) yield* iterateDepsForMeta(meta);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
/** Iterate deps */
|
|
1007
|
+
function* iterateDepsForMeta(meta) {
|
|
1008
|
+
if (!meta) return;
|
|
1009
|
+
for (const depName of DEPS) {
|
|
1010
|
+
const deps = meta[depName];
|
|
1011
|
+
if (!deps) continue;
|
|
1012
|
+
for (const [n, v] of Object.entries(deps)) if (typeof v === "string") yield {
|
|
1013
|
+
name: n,
|
|
1014
|
+
ver: v,
|
|
1015
|
+
packageJsonPath: meta._where
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
var no_restricted_deps_default = createRule("no-restricted-deps", {
|
|
1022
|
+
meta: {
|
|
1023
|
+
docs: {
|
|
1024
|
+
description: "Disallows dependence on the specified package.",
|
|
1025
|
+
category: "Best Practices",
|
|
1026
|
+
recommended: false
|
|
1027
|
+
},
|
|
1028
|
+
schema: {
|
|
1029
|
+
type: "array",
|
|
1030
|
+
items: { oneOf: [{ type: "string" }, {
|
|
1031
|
+
type: "object",
|
|
1032
|
+
properties: {
|
|
1033
|
+
package: { type: "string" },
|
|
1034
|
+
version: { type: "string" },
|
|
1035
|
+
message: { type: "string" },
|
|
1036
|
+
deep: { enum: ["local", "server"] }
|
|
1037
|
+
},
|
|
1038
|
+
required: ["package"],
|
|
1039
|
+
additionalProperties: false
|
|
1040
|
+
}] },
|
|
1041
|
+
uniqueItems: true,
|
|
1042
|
+
minItems: 0
|
|
1043
|
+
},
|
|
1044
|
+
messages: { restricted: "{{message}}" },
|
|
1045
|
+
type: "suggestion"
|
|
1046
|
+
},
|
|
1047
|
+
create(context) {
|
|
1048
|
+
if (!context.sourceCode.parserServices.isJSON) return {};
|
|
1049
|
+
const validateForPackage = parseOptions(context.options);
|
|
1050
|
+
/** Define dependency visitor */
|
|
1051
|
+
function defineVisitor(_depsName) {
|
|
1052
|
+
return (node) => {
|
|
1053
|
+
const result = validateForPackage(String(getKeyFromJSONProperty(node)), String(getStaticJSONValue(node.value)));
|
|
1054
|
+
if (!result) return;
|
|
1055
|
+
context.report({
|
|
1056
|
+
loc: node.loc,
|
|
1057
|
+
messageId: "restricted",
|
|
1058
|
+
data: result
|
|
1059
|
+
});
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
return defineJsonVisitor({
|
|
1063
|
+
dependencies: defineVisitor("dependencies"),
|
|
1064
|
+
peerDependencies: defineVisitor("peerDependencies"),
|
|
1065
|
+
optionalDependencies: defineVisitor("optionalDependencies"),
|
|
1066
|
+
devDependencies: defineVisitor("devDependencies")
|
|
1067
|
+
});
|
|
1068
|
+
/** Checks whether the given vers are match or not */
|
|
1069
|
+
function matchVersions(version, optionVersion) {
|
|
1070
|
+
if (!optionVersion) return true;
|
|
1071
|
+
const range = getSemverRange(version);
|
|
1072
|
+
return Boolean(range && intersects(range, optionVersion));
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Parse option
|
|
1076
|
+
*/
|
|
1077
|
+
function parseOption(option) {
|
|
1078
|
+
if (typeof option === "string") {
|
|
1079
|
+
const regexp = toRegExp(option);
|
|
1080
|
+
return (n, _v) => {
|
|
1081
|
+
if (regexp.test(n)) return { message: `Depend on '${option}' is not allowed.` };
|
|
1082
|
+
return null;
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
const regexp = toRegExp(option.package);
|
|
1086
|
+
const version = option.version ? getSemverRange(option.version) : null;
|
|
1087
|
+
const validator = (n, v) => {
|
|
1088
|
+
if (regexp.test(n) && matchVersions(v, version)) return { message: option.message || buildDefaultMessage(option) };
|
|
1089
|
+
return null;
|
|
1090
|
+
};
|
|
1091
|
+
if (!option.deep) return validator;
|
|
1092
|
+
return new DeepValidateContext(context, validator).buildDeepValidator(option.deep);
|
|
1093
|
+
/** Build default message for object option */
|
|
1094
|
+
function buildDefaultMessage(objectOption) {
|
|
1095
|
+
const versionForMessage = objectOption.version ? `@${objectOption.version}` : "";
|
|
1096
|
+
if (objectOption.package.startsWith("/") && versionForMessage) return `Depend on '${objectOption.package} ${versionForMessage}' is not allowed.`;
|
|
1097
|
+
return `Depend on '${objectOption.package}${versionForMessage}' is not allowed.`;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Parse options
|
|
1102
|
+
*/
|
|
1103
|
+
function parseOptions(options) {
|
|
1104
|
+
const validators = options.map(parseOption);
|
|
1105
|
+
return (packageName, version) => {
|
|
1106
|
+
for (const validator of validators) {
|
|
1107
|
+
const result = validator(packageName, version);
|
|
1108
|
+
if (result) return result;
|
|
1109
|
+
}
|
|
1110
|
+
return null;
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
//#endregion
|
|
1117
|
+
//#region lib/utils/semver-range.ts
|
|
1118
|
+
/**
|
|
1119
|
+
* Iterate version ranges
|
|
1120
|
+
*/
|
|
1121
|
+
function* iterateSemverRanges(versionRanges) {
|
|
1122
|
+
let startOffset = 0;
|
|
1123
|
+
for (const strRange of versionRanges.split("||")) {
|
|
1124
|
+
const result = toRangeResult(strRange, startOffset);
|
|
1125
|
+
if (result) yield result;
|
|
1126
|
+
startOffset += strRange.length + 2;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Build result object
|
|
1131
|
+
*/
|
|
1132
|
+
function toRangeResult(strRange, startOffset) {
|
|
1133
|
+
let start = 0;
|
|
1134
|
+
while (strRange.length > start && !strRange[start].trim()) start++;
|
|
1135
|
+
let end = strRange.length;
|
|
1136
|
+
while (end > start && !strRange[end - 1].trim()) end--;
|
|
1137
|
+
const value = strRange.slice(start, end);
|
|
1138
|
+
const range = getSemverRange(value);
|
|
1139
|
+
if (!range) return null;
|
|
1140
|
+
if (range.set.length !== 1) return null;
|
|
1141
|
+
return {
|
|
1142
|
+
value,
|
|
1143
|
+
comparators: range.set[0],
|
|
1144
|
+
range: [startOffset + start, startOffset + end]
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
//#endregion
|
|
1149
|
+
//#region lib/rules/prefer-caret-range-version.ts
|
|
1150
|
+
var prefer_caret_range_version_default = createRule("prefer-caret-range-version", {
|
|
1151
|
+
meta: {
|
|
1152
|
+
docs: {
|
|
1153
|
+
description: "require caret(`^`) version instead of range version.",
|
|
1154
|
+
category: "Stylistic Issues",
|
|
1155
|
+
recommended: false
|
|
1156
|
+
},
|
|
1157
|
+
fixable: "code",
|
|
1158
|
+
schema: [],
|
|
1159
|
+
messages: {},
|
|
1160
|
+
type: "suggestion"
|
|
1161
|
+
},
|
|
1162
|
+
create(context) {
|
|
1163
|
+
const sourceCode = context.sourceCode;
|
|
1164
|
+
if (!sourceCode.parserServices.isJSON) return {};
|
|
1165
|
+
/**
|
|
1166
|
+
* Convert to use caret range syntax.
|
|
1167
|
+
*/
|
|
1168
|
+
function convertToUseCaret(range) {
|
|
1169
|
+
if (range.comparators.length !== 2) return null;
|
|
1170
|
+
const start = range.comparators.find((c) => c.operator === ">=");
|
|
1171
|
+
const end = range.comparators.find((c) => c.operator === "<");
|
|
1172
|
+
if (!start || !end) return null;
|
|
1173
|
+
const caretRangeText = `^${start.semver.version}`;
|
|
1174
|
+
const caretRange = getSemverRange(caretRangeText);
|
|
1175
|
+
if (!caretRange) return null;
|
|
1176
|
+
const caretEnd = caretRange.set[0].find((c) => c.operator === "<");
|
|
1177
|
+
if (!caretEnd) return null;
|
|
1178
|
+
if (caretEnd.semver.compare(end.semver) !== 0) {
|
|
1179
|
+
const endPre = new SemVer(`${end.semver.version}-0`);
|
|
1180
|
+
if (caretEnd.semver.compare(endPre) !== 0) return null;
|
|
1181
|
+
}
|
|
1182
|
+
return caretRangeText;
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Verify for range
|
|
1186
|
+
*/
|
|
1187
|
+
function verifyRange(range, node) {
|
|
1188
|
+
const fixedRange = convertToUseCaret(range);
|
|
1189
|
+
if (!fixedRange) return;
|
|
1190
|
+
context.report({
|
|
1191
|
+
loc: {
|
|
1192
|
+
start: sourceCode.getLocFromIndex(node.range[0] + range.range[0]),
|
|
1193
|
+
end: sourceCode.getLocFromIndex(node.range[0] + range.range[1])
|
|
1194
|
+
},
|
|
1195
|
+
message: `Use '${fixedRange}' syntax instead.`,
|
|
1196
|
+
fix(fixer) {
|
|
1197
|
+
return fixer.replaceTextRange(node.range, JSON.stringify(node.value.slice(0, range.range[0]) + fixedRange + node.value.slice(range.range[1])));
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Verify for version
|
|
1203
|
+
*/
|
|
1204
|
+
function verifyVersion(node) {
|
|
1205
|
+
if (maybeDepId$1(node.value)) return;
|
|
1206
|
+
for (const range of iterateSemverRanges(node.value)) {
|
|
1207
|
+
const lowerRange = range.value.toLowerCase();
|
|
1208
|
+
if (!lowerRange.startsWith("~")) {
|
|
1209
|
+
if (lowerRange.startsWith("^") || /^\d+$/u.test(range.value) || (range.value.endsWith(".x") || range.value.endsWith(".*")) && !/\s+-\s+/u.test(range.value)) continue;
|
|
1210
|
+
}
|
|
1211
|
+
verifyRange(range, node);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
return defineJsonVisitor({ "engines, dependencies, peerDependencies, devDependencies, optionalDependencies"(node) {
|
|
1215
|
+
if (isJSONStringLiteral$1(node.value)) verifyVersion(node.value);
|
|
1216
|
+
} });
|
|
1217
|
+
}
|
|
1218
|
+
});
|
|
1219
|
+
/**
|
|
1220
|
+
* Checks whether the given expression is string literal or not
|
|
1221
|
+
*/
|
|
1222
|
+
function isJSONStringLiteral$1(node) {
|
|
1223
|
+
return node.type === "JSONLiteral" && typeof node.value === "string";
|
|
1224
|
+
}
|
|
1225
|
+
/** Checks whether the given ver is dependencies identify */
|
|
1226
|
+
function maybeDepId$1(ver) {
|
|
1227
|
+
return ver.includes("/") || ver.includes(":") || /^[-a-z]+$/.test(ver);
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
//#endregion
|
|
1231
|
+
//#region lib/rules/prefer-tilde-range-version.ts
|
|
1232
|
+
var prefer_tilde_range_version_default = createRule("prefer-tilde-range-version", {
|
|
1233
|
+
meta: {
|
|
1234
|
+
docs: {
|
|
1235
|
+
description: "require tilde(`~`) version instead of range version.",
|
|
1236
|
+
category: "Stylistic Issues",
|
|
1237
|
+
recommended: false
|
|
1238
|
+
},
|
|
1239
|
+
fixable: "code",
|
|
1240
|
+
schema: [],
|
|
1241
|
+
messages: {},
|
|
1242
|
+
type: "suggestion"
|
|
1243
|
+
},
|
|
1244
|
+
create(context) {
|
|
1245
|
+
const sourceCode = context.sourceCode;
|
|
1246
|
+
if (!sourceCode.parserServices.isJSON) return {};
|
|
1247
|
+
/**
|
|
1248
|
+
* Convert to use tilde range syntax.
|
|
1249
|
+
*/
|
|
1250
|
+
function convertToUseTilde(range) {
|
|
1251
|
+
if (range.comparators.length !== 2) return null;
|
|
1252
|
+
const start = range.comparators.find((c) => c.operator === ">=");
|
|
1253
|
+
const end = range.comparators.find((c) => c.operator === "<");
|
|
1254
|
+
if (!start || !end) return null;
|
|
1255
|
+
const tildeRangeText = `~${start.semver.version}`;
|
|
1256
|
+
const tildeRange = getSemverRange(tildeRangeText);
|
|
1257
|
+
if (!tildeRange) return null;
|
|
1258
|
+
const tildeEnd = tildeRange.set[0].find((c) => c.operator === "<");
|
|
1259
|
+
if (!tildeEnd) return null;
|
|
1260
|
+
if (tildeEnd.semver.compare(end.semver) !== 0) {
|
|
1261
|
+
const endPre = new SemVer(`${end.semver.version}-0`);
|
|
1262
|
+
if (tildeEnd.semver.compare(endPre) !== 0) return null;
|
|
1263
|
+
}
|
|
1264
|
+
return tildeRangeText;
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Verify for range
|
|
1268
|
+
*/
|
|
1269
|
+
function verifyRange(range, node) {
|
|
1270
|
+
const fixedRange = convertToUseTilde(range);
|
|
1271
|
+
if (!fixedRange) return;
|
|
1272
|
+
context.report({
|
|
1273
|
+
loc: {
|
|
1274
|
+
start: sourceCode.getLocFromIndex(node.range[0] + range.range[0]),
|
|
1275
|
+
end: sourceCode.getLocFromIndex(node.range[0] + range.range[1])
|
|
1276
|
+
},
|
|
1277
|
+
message: `Use '${fixedRange}' syntax instead.`,
|
|
1278
|
+
fix(fixer) {
|
|
1279
|
+
return fixer.replaceTextRange(node.range, JSON.stringify(node.value.slice(0, range.range[0]) + fixedRange + node.value.slice(range.range[1])));
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Verify for version
|
|
1285
|
+
*/
|
|
1286
|
+
function verifyVersion(node) {
|
|
1287
|
+
if (maybeDepId(node.value)) return;
|
|
1288
|
+
for (const range of iterateSemverRanges(node.value)) {
|
|
1289
|
+
const lowerRange = range.value.toLowerCase();
|
|
1290
|
+
if (!lowerRange.startsWith("^")) {
|
|
1291
|
+
if (lowerRange.startsWith("~") || /^\d+$/u.test(range.value) || /^\d+\.\d+$/u.test(range.value) || (range.value.endsWith(".x") || range.value.endsWith(".*")) && !/\s+-\s+/u.test(range.value)) continue;
|
|
1292
|
+
}
|
|
1293
|
+
verifyRange(range, node);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
return defineJsonVisitor({ "engines, dependencies, peerDependencies, devDependencies, optionalDependencies"(node) {
|
|
1297
|
+
if (isJSONStringLiteral(node.value)) verifyVersion(node.value);
|
|
1298
|
+
} });
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
1301
|
+
/**
|
|
1302
|
+
* Checks whether the given expression is string literal or not
|
|
1303
|
+
*/
|
|
1304
|
+
function isJSONStringLiteral(node) {
|
|
1305
|
+
return node.type === "JSONLiteral" && typeof node.value === "string";
|
|
1306
|
+
}
|
|
1307
|
+
/** Checks whether the given ver is dependencies identify */
|
|
1308
|
+
function maybeDepId(ver) {
|
|
1309
|
+
return ver.includes("/") || ver.includes(":") || /^[-a-z]+$/.test(ver);
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
//#endregion
|
|
1313
|
+
//#region lib/rules/require-provenance-deps.ts
|
|
1314
|
+
var require_provenance_deps_default = createRule("require-provenance-deps", {
|
|
1315
|
+
meta: {
|
|
1316
|
+
docs: {
|
|
1317
|
+
description: "Require provenance information for dependencies",
|
|
1318
|
+
category: "Best Practices",
|
|
1319
|
+
recommended: false
|
|
1320
|
+
},
|
|
1321
|
+
schema: [{
|
|
1322
|
+
type: "object",
|
|
1323
|
+
properties: {
|
|
1324
|
+
devDependencies: { type: "boolean" },
|
|
1325
|
+
allows: {
|
|
1326
|
+
type: "array",
|
|
1327
|
+
items: { type: "string" },
|
|
1328
|
+
uniqueItems: true
|
|
1329
|
+
}
|
|
1330
|
+
},
|
|
1331
|
+
additionalProperties: false
|
|
1332
|
+
}],
|
|
1333
|
+
messages: { missingProvenance: "Dependency \"{{name}}\" has versions without provenance information: {{versions}}." },
|
|
1334
|
+
type: "suggestion"
|
|
1335
|
+
},
|
|
1336
|
+
create(context) {
|
|
1337
|
+
if (!context.sourceCode.parserServices.isJSON) return {};
|
|
1338
|
+
const devDependencies = Boolean(context.options[0]?.devDependencies);
|
|
1339
|
+
/**
|
|
1340
|
+
* Extract version ranges without provenance information
|
|
1341
|
+
*/
|
|
1342
|
+
function extractNoProvenanceRanges(meta) {
|
|
1343
|
+
const noProvenanceRanges = [];
|
|
1344
|
+
let prev = null;
|
|
1345
|
+
for (let index = 0; index < meta.length; index++) {
|
|
1346
|
+
const m = meta[index];
|
|
1347
|
+
if (m.dist?.attestations?.provenance) {
|
|
1348
|
+
prev = null;
|
|
1349
|
+
continue;
|
|
1350
|
+
}
|
|
1351
|
+
if (prev == null) {
|
|
1352
|
+
prev = [m.version, m.version];
|
|
1353
|
+
noProvenanceRanges.push(prev);
|
|
1354
|
+
} else prev[1] = m.version;
|
|
1355
|
+
}
|
|
1356
|
+
return noProvenanceRanges;
|
|
1357
|
+
}
|
|
1358
|
+
return defineJsonVisitor({ [devDependencies ? "dependencies, peerDependencies, devDependencies" : "dependencies, peerDependencies"](node) {
|
|
1359
|
+
const name = getKeyFromJSONProperty(node);
|
|
1360
|
+
const ver = getStaticJSONValue(node.value);
|
|
1361
|
+
if (typeof name !== "string" || typeof ver !== "string" || !ver) return;
|
|
1362
|
+
if (context.options[0]?.allows?.includes(name)) return;
|
|
1363
|
+
const meta = getMetaFromNpm(name, ver).get();
|
|
1364
|
+
if (!meta || !meta.length) return;
|
|
1365
|
+
const noProvenanceRanges = extractNoProvenanceRanges(meta);
|
|
1366
|
+
if (noProvenanceRanges.length === 0) return;
|
|
1367
|
+
context.report({
|
|
1368
|
+
loc: node.loc,
|
|
1369
|
+
messageId: "missingProvenance",
|
|
1370
|
+
data: {
|
|
1371
|
+
name,
|
|
1372
|
+
versions: formatList(noProvenanceRanges.map(([from, to]) => from === to ? `${from}` : `${from} - ${to}`))
|
|
1373
|
+
}
|
|
1374
|
+
});
|
|
1375
|
+
} });
|
|
1376
|
+
/**
|
|
1377
|
+
* Format list of strings into a human-readable string
|
|
1378
|
+
*/
|
|
1379
|
+
function formatList(items) {
|
|
1380
|
+
return items.length <= 2 ? items.join(" and ") : `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`;
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
});
|
|
1384
|
+
|
|
1385
|
+
//#endregion
|
|
1386
|
+
//#region lib/rules/valid-engines.ts
|
|
1387
|
+
var valid_engines_default = createRule("valid-engines", {
|
|
1388
|
+
meta: {
|
|
1389
|
+
...compat_engines_default.meta,
|
|
1390
|
+
docs: { ...compat_engines_default.meta.docs },
|
|
1391
|
+
deprecated: true,
|
|
1392
|
+
replacedBy: ["compat-engines"]
|
|
1393
|
+
},
|
|
1394
|
+
create: compat_engines_default.create
|
|
1395
|
+
});
|
|
1396
|
+
|
|
1397
|
+
//#endregion
|
|
1398
|
+
//#region lib/rules/valid-semver.ts
|
|
1399
|
+
var valid_semver_default = createRule("valid-semver", {
|
|
1400
|
+
meta: {
|
|
1401
|
+
docs: {
|
|
1402
|
+
description: "enforce versions that is valid as a semantic version.",
|
|
1403
|
+
category: "Possible Errors",
|
|
1404
|
+
recommended: true
|
|
1405
|
+
},
|
|
1406
|
+
schema: [],
|
|
1407
|
+
messages: {},
|
|
1408
|
+
type: "problem"
|
|
1409
|
+
},
|
|
1410
|
+
create(context) {
|
|
1411
|
+
if (!context.sourceCode.parserServices.isJSON) return {};
|
|
1412
|
+
return defineJsonVisitor({
|
|
1413
|
+
engines(node) {
|
|
1414
|
+
const ver = getStaticJSONValue(node.value);
|
|
1415
|
+
if (typeof ver !== "string" || ver == null) {
|
|
1416
|
+
context.report({
|
|
1417
|
+
loc: node.value.loc,
|
|
1418
|
+
message: `\`${JSON.stringify(ver)}\` is invalid.`
|
|
1419
|
+
});
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
if (getSemverRange(ver) == null) context.report({
|
|
1423
|
+
loc: node.value.loc,
|
|
1424
|
+
message: `"${ver}" is invalid.`
|
|
1425
|
+
});
|
|
1426
|
+
},
|
|
1427
|
+
"dependencies, peerDependencies, devDependencies, optionalDependencies"(node) {
|
|
1428
|
+
if (typeof getKeyFromJSONProperty(node) !== "string") return;
|
|
1429
|
+
const ver = getStaticJSONValue(node.value);
|
|
1430
|
+
if (typeof ver !== "string" || ver == null) {
|
|
1431
|
+
context.report({
|
|
1432
|
+
loc: node.value.loc,
|
|
1433
|
+
message: `\`${JSON.stringify(ver)}\` is invalid.`
|
|
1434
|
+
});
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
if (maybeNotRange(ver)) return;
|
|
1438
|
+
if (getSemverRange(ver) == null) context.report({
|
|
1439
|
+
loc: node.value.loc,
|
|
1440
|
+
message: `"${ver}" is invalid.`
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
});
|
|
1446
|
+
/** Checks whether the given version string is not version range */
|
|
1447
|
+
function maybeNotRange(ver) {
|
|
1448
|
+
if (ver.startsWith(".") || ver.startsWith("~") || ver.includes("/")) return true;
|
|
1449
|
+
if (ver.includes(":")) return true;
|
|
1450
|
+
if (/^[-a-z]+$/.test(ver)) return true;
|
|
1451
|
+
return false;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
//#endregion
|
|
1455
|
+
//#region lib/utils/rules.ts
|
|
1456
|
+
const rules$1 = [
|
|
1457
|
+
absolute_version_default,
|
|
1458
|
+
compat_engines_default,
|
|
1459
|
+
no_deprecated_default,
|
|
1460
|
+
no_dupe_deps_default,
|
|
1461
|
+
no_restricted_deps_default,
|
|
1462
|
+
prefer_caret_range_version_default,
|
|
1463
|
+
prefer_tilde_range_version_default,
|
|
1464
|
+
require_provenance_deps_default,
|
|
1465
|
+
valid_engines_default,
|
|
1466
|
+
valid_semver_default
|
|
1467
|
+
];
|
|
1468
|
+
|
|
1469
|
+
//#endregion
|
|
1470
|
+
//#region lib/configs/rules/recommended.ts
|
|
1471
|
+
var recommended_default = { rules: {
|
|
1472
|
+
"node-dependencies/compat-engines": "error",
|
|
1473
|
+
"node-dependencies/no-dupe-deps": "error",
|
|
1474
|
+
"node-dependencies/valid-semver": "error"
|
|
1475
|
+
} };
|
|
1476
|
+
|
|
1477
|
+
//#endregion
|
|
1478
|
+
//#region lib/configs/flat/recommended.ts
|
|
1479
|
+
const recommendedConfig = [{ plugins: { get "node-dependencies"() {
|
|
1480
|
+
return lib_default;
|
|
1481
|
+
} } }, {
|
|
1482
|
+
files: ["**/package.json", "package.json"],
|
|
1483
|
+
languageOptions: { parser: jsonParser },
|
|
1484
|
+
rules: recommended_default.rules
|
|
1485
|
+
}];
|
|
1486
|
+
|
|
1487
|
+
//#endregion
|
|
1488
|
+
//#region package.json
|
|
1489
|
+
var package_default = {
|
|
1490
|
+
name: "eslint-plugin-node-dependencies",
|
|
1491
|
+
version: "2.0.0",
|
|
1492
|
+
type: "module",
|
|
1493
|
+
description: "ESLint plugin to check Node.js dependencies.",
|
|
1494
|
+
repository: "git+https://github.com/ota-meshi/eslint-plugin-node-dependencies.git",
|
|
1495
|
+
homepage: "https://github.com/ota-meshi/eslint-plugin-node-dependencies#readme",
|
|
1496
|
+
author: "Yosuke Ota (https://github.com/ota-meshi)",
|
|
1497
|
+
maintainers: ["JounQin <admin@1stg.me> (https://www.1stG.me)"],
|
|
1498
|
+
funding: ["https://github.com/sponsors/ota-meshi", "https://github.com/sponsors/JounQin"],
|
|
1499
|
+
license: "MIT",
|
|
1500
|
+
engines: { "node": "^20.19.0 || ^22.13.0 || >=24" },
|
|
1501
|
+
exports: {
|
|
1502
|
+
".": {
|
|
1503
|
+
"types": "./dist/index.d.mts",
|
|
1504
|
+
"import": "./dist/index.mjs",
|
|
1505
|
+
"default": "./dist/index.mjs"
|
|
1506
|
+
},
|
|
1507
|
+
"./package.json": "./package.json"
|
|
1508
|
+
},
|
|
1509
|
+
files: ["dist"],
|
|
1510
|
+
keywords: [
|
|
1511
|
+
"eslint",
|
|
1512
|
+
"eslintplugin",
|
|
1513
|
+
"eslint-plugin",
|
|
1514
|
+
"nodejs",
|
|
1515
|
+
"dependencies",
|
|
1516
|
+
"json"
|
|
1517
|
+
],
|
|
1518
|
+
scripts: {
|
|
1519
|
+
"build": "npm run build:ts",
|
|
1520
|
+
"build:ts": "tsdown",
|
|
1521
|
+
"clean": "node -e \"import('fs').then(fs=>['dist','coverage'].forEach(d=>{try{fs.rmSync(d,{recursive:true,force:true})}catch(e){}}))\"",
|
|
1522
|
+
"docs:build": "npm run build && env-cmd -e dev -- vitepress build docs",
|
|
1523
|
+
"docs:build-and-preview": "npm run docs:build && npx http-server docs/.vitepress/dist",
|
|
1524
|
+
"docs:watch": "npm run build && env-cmd -e dev -- vitepress dev docs",
|
|
1525
|
+
"eslint-fix": "npm run lint:eslint -- --fix",
|
|
1526
|
+
"lint": "npm run lint:eslint && npm run lint:tsc",
|
|
1527
|
+
"lint:eslint": "eslint . --ext .js,.vue,.ts,.json,.yaml,.yml",
|
|
1528
|
+
"lint:tsc": "tsc",
|
|
1529
|
+
"mocha": "npm run ts -- ./node_modules/mocha/bin/mocha.js",
|
|
1530
|
+
"new": "node --experimental-strip-types --experimental-transform-types ./tools/new-rule.ts",
|
|
1531
|
+
"prebuild": "npm run -s clean",
|
|
1532
|
+
"preinstall": "npx only-allow npm",
|
|
1533
|
+
"prerelease": "npm run test && npm run build",
|
|
1534
|
+
"pretest": "npm run build",
|
|
1535
|
+
"preversion": "npm test && git add .",
|
|
1536
|
+
"release": "clean-pkg-json -r scripts.preinstall && changeset publish",
|
|
1537
|
+
"test": "npm run test:c8",
|
|
1538
|
+
"test:base": "npm run mocha -- \"tests/**/*.ts\" --reporter dot --timeout 60000",
|
|
1539
|
+
"test:c8": "c8 --reporter=lcov npm run test:base",
|
|
1540
|
+
"test:debug": "npm run mocha -- \"tests/**/*.ts\" --reporter dot --timeout 60000",
|
|
1541
|
+
"test:watch": "npm run test:base -- --watch",
|
|
1542
|
+
"ts": "node --import @oxc-node/core/register",
|
|
1543
|
+
"update": "npm run ts -- ./tools/update.ts && npm run eslint-fix",
|
|
1544
|
+
"version": "env-cmd -e version -- npm run update && git add .",
|
|
1545
|
+
"version:ci": "changeset version && env-cmd -e version-ci -- npm run update && git add --all"
|
|
1546
|
+
},
|
|
1547
|
+
peerDependencies: { "eslint": ">=9.38.0" },
|
|
1548
|
+
dependencies: {
|
|
1549
|
+
"jsonc-eslint-parser": "^3.1.0",
|
|
1550
|
+
"npm-package-arg": "^13.0.2",
|
|
1551
|
+
"package-json": "^10.0.1",
|
|
1552
|
+
"semver": "^7.7.4",
|
|
1553
|
+
"synckit": "^0.11.12",
|
|
1554
|
+
"undici": "^7.22.0"
|
|
1555
|
+
},
|
|
1556
|
+
devDependencies: {
|
|
1557
|
+
"@changesets/changelog-github": "^0.5.0",
|
|
1558
|
+
"@changesets/cli": "^2.24.2",
|
|
1559
|
+
"@eslint-community/eslint-utils": "^4.6.1",
|
|
1560
|
+
"@ota-meshi/eslint-plugin": "^0.20.0",
|
|
1561
|
+
"@oxc-node/core": "^0.0.35",
|
|
1562
|
+
"@shikijs/vitepress-twoslash": "^3.0.0",
|
|
1563
|
+
"@swc-node/register": "^1.10.10",
|
|
1564
|
+
"@types/chai": "^5.0.0",
|
|
1565
|
+
"@types/eslint-scope": "^8.0.0",
|
|
1566
|
+
"@types/estree": "^1.0.0",
|
|
1567
|
+
"@types/global-agent": "^3.0.0",
|
|
1568
|
+
"@types/mocha": "^10.0.0",
|
|
1569
|
+
"@types/node": "^24.0.0",
|
|
1570
|
+
"@types/npm-package-arg": "^6.1.1",
|
|
1571
|
+
"@types/semver": "^7.3.8",
|
|
1572
|
+
"c8": "^10.0.0",
|
|
1573
|
+
"chai": "^6.0.0",
|
|
1574
|
+
"clean-pkg-json": "^1.3.0",
|
|
1575
|
+
"env-cmd": "^11.0.0",
|
|
1576
|
+
"eslint": "^10.0.0",
|
|
1577
|
+
"eslint-config-prettier": "^10.0.0",
|
|
1578
|
+
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
1579
|
+
"eslint-plugin-eslint-plugin": "^7.0.0",
|
|
1580
|
+
"eslint-plugin-jsdoc": "^62.0.0",
|
|
1581
|
+
"eslint-plugin-json-schema-validator": "^6.0.0",
|
|
1582
|
+
"eslint-plugin-jsonc": "^3.0.0",
|
|
1583
|
+
"eslint-plugin-n": "^17.0.0",
|
|
1584
|
+
"eslint-plugin-node-dependencies": ".",
|
|
1585
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
1586
|
+
"eslint-plugin-regexp": "^3.0.0",
|
|
1587
|
+
"eslint-plugin-vue": "^10.0.0",
|
|
1588
|
+
"eslint-plugin-yml": "^3.0.0",
|
|
1589
|
+
"mocha": "^11.0.0",
|
|
1590
|
+
"only-allow": "^1.2.1",
|
|
1591
|
+
"prettier": "^3.0.0",
|
|
1592
|
+
"prettier-plugin-pkg": "^0.21.0",
|
|
1593
|
+
"stylelint": "^17.0.0",
|
|
1594
|
+
"stylelint-config-recommended-vue": "^1.0.0",
|
|
1595
|
+
"stylelint-config-standard": "^40.0.0",
|
|
1596
|
+
"stylelint-config-standard-vue": "^1.0.0",
|
|
1597
|
+
"stylelint-stylus": "^1.0.0",
|
|
1598
|
+
"tsdown": "^0.20.3",
|
|
1599
|
+
"twoslash-eslint": "^0.3.0",
|
|
1600
|
+
"typescript": "^5.0.0",
|
|
1601
|
+
"typescript-eslint": "^8.31.0",
|
|
1602
|
+
"vitepress": "^1.0.1"
|
|
1603
|
+
},
|
|
1604
|
+
overrides: { "@shikijs/types": "^3.0.0" },
|
|
1605
|
+
publishConfig: { "access": "public" }
|
|
1606
|
+
};
|
|
1607
|
+
|
|
1608
|
+
//#endregion
|
|
1609
|
+
//#region lib/meta.ts
|
|
1610
|
+
var meta_exports = /* @__PURE__ */ __exportAll({
|
|
1611
|
+
name: () => name,
|
|
1612
|
+
version: () => version
|
|
1613
|
+
});
|
|
1614
|
+
const { name, version } = package_default;
|
|
1615
|
+
|
|
1616
|
+
//#endregion
|
|
1617
|
+
//#region lib/index.ts
|
|
1618
|
+
const configs = {
|
|
1619
|
+
recommended: recommendedConfig,
|
|
1620
|
+
"flat/recommended": recommendedConfig
|
|
1621
|
+
};
|
|
1622
|
+
const rules = rules$1.reduce((obj, r) => {
|
|
1623
|
+
obj[r.meta.docs.ruleName] = r;
|
|
1624
|
+
return obj;
|
|
1625
|
+
}, {});
|
|
1626
|
+
var lib_default = {
|
|
1627
|
+
meta: meta_exports,
|
|
1628
|
+
configs,
|
|
1629
|
+
rules
|
|
1630
|
+
};
|
|
1631
|
+
|
|
1632
|
+
//#endregion
|
|
1633
|
+
export { configs, lib_default as default, meta_exports as meta, rules };
|