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.
Files changed (57) hide show
  1. package/README.md +10 -66
  2. package/dist/chunk-C7Uep-_p.mjs +20 -0
  3. package/dist/index.d.mts +28 -0
  4. package/dist/index.mjs +1633 -0
  5. package/dist/worker.d.mts +7 -0
  6. package/dist/worker.mjs +30 -0
  7. package/package.json +17 -10
  8. package/dist/configs/flat/recommended.d.ts +0 -17
  9. package/dist/configs/flat/recommended.js +0 -57
  10. package/dist/configs/recommended.d.ts +0 -9
  11. package/dist/configs/recommended.js +0 -15
  12. package/dist/configs/rules/recommended.d.ts +0 -5
  13. package/dist/configs/rules/recommended.js +0 -9
  14. package/dist/index.d.ts +0 -34
  15. package/dist/index.js +0 -54
  16. package/dist/meta.d.ts +0 -1
  17. package/dist/meta.js +0 -5
  18. package/dist/rules/absolute-version.d.ts +0 -2
  19. package/dist/rules/absolute-version.js +0 -160
  20. package/dist/rules/compat-engines.d.ts +0 -2
  21. package/dist/rules/compat-engines.js +0 -262
  22. package/dist/rules/no-deprecated.d.ts +0 -2
  23. package/dist/rules/no-deprecated.js +0 -62
  24. package/dist/rules/no-dupe-deps.d.ts +0 -2
  25. package/dist/rules/no-dupe-deps.js +0 -89
  26. package/dist/rules/no-restricted-deps.d.ts +0 -2
  27. package/dist/rules/no-restricted-deps.js +0 -258
  28. package/dist/rules/prefer-caret-range-version.d.ts +0 -2
  29. package/dist/rules/prefer-caret-range-version.js +0 -98
  30. package/dist/rules/prefer-tilde-range-version.d.ts +0 -2
  31. package/dist/rules/prefer-tilde-range-version.js +0 -99
  32. package/dist/rules/require-provenance-deps.d.ts +0 -2
  33. package/dist/rules/require-provenance-deps.js +0 -97
  34. package/dist/rules/valid-engines.d.ts +0 -2
  35. package/dist/rules/valid-engines.js +0 -11
  36. package/dist/rules/valid-semver.d.ts +0 -2
  37. package/dist/rules/valid-semver.js +0 -77
  38. package/dist/types.d.ts +0 -51
  39. package/dist/types.js +0 -2
  40. package/dist/utils/ast-utils.d.ts +0 -3
  41. package/dist/utils/ast-utils.js +0 -24
  42. package/dist/utils/index.d.ts +0 -5
  43. package/dist/utils/index.js +0 -72
  44. package/dist/utils/meta.d.ts +0 -30
  45. package/dist/utils/meta.js +0 -274
  46. package/dist/utils/package-json/index.d.ts +0 -1
  47. package/dist/utils/package-json/index.js +0 -17
  48. package/dist/utils/package-json/worker.d.mts +0 -1
  49. package/dist/utils/package-json/worker.mjs +0 -26
  50. package/dist/utils/regexp.d.ts +0 -3
  51. package/dist/utils/regexp.js +0 -20
  52. package/dist/utils/rules.d.ts +0 -2
  53. package/dist/utils/rules.js +0 -28
  54. package/dist/utils/semver-range.d.ts +0 -7
  55. package/dist/utils/semver-range.js +0 -37
  56. package/dist/utils/semver.d.ts +0 -7
  57. 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 };