eslint-plugin-n 17.5.1 → 17.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,11 +18,11 @@ Additional ESLint rules for Node.js
18
18
  npm install --save-dev eslint eslint-plugin-n
19
19
  ```
20
20
 
21
- | Version | Supported Node.js | Supported ESLint Version |
22
- |---------|-------------------|---------------------------|
23
- | 17.x | `^18.18.0 \|\| ^20.9.0 \|\| >=21.1.0` | `>=8.23.0` |
24
- | 16.x | `>=16.0.0` | `>=7.0.0` |
25
- | 15.x | `>=12.22.0` | `>=7.0.0` |
21
+ | Version | Supported Node.js | Supported ESLint Version | Status |
22
+ |---------|-------------------|---------------------------|--------|
23
+ | 17.x | `^18.18.0 \|\| ^20.9.0 \|\| >=21.1.0` | `>=8.23.0` | 🏃‍♂️actively maintained |
24
+ | 16.x | `>=16.0.0` | `>=7.0.0` | ⚠️EOL |
25
+ | 15.x | `>=12.22.0` | `>=7.0.0` | ⚠️EOL |
26
26
 
27
27
  **Note:** It recommends a use of [the "engines" field of package.json](https://docs.npmjs.com/files/package.json#engines). The "engines" field is used by `n/no-unsupported-features/*` rules.
28
28
 
@@ -12,10 +12,50 @@ const { getPackageJson } = require("../util/get-package-json")
12
12
  const getNpmignore = require("../util/get-npmignore")
13
13
  const { isBinFile } = require("../util/is-bin-file")
14
14
 
15
- const NODE_SHEBANG = "#!/usr/bin/env node\n"
15
+ const ENV_SHEBANG = "#!/usr/bin/env"
16
+ const NODE_SHEBANG = `${ENV_SHEBANG} {{executableName}}\n`
16
17
  const SHEBANG_PATTERN = /^(#!.+?)?(\r)?\n/u
17
- const NODE_SHEBANG_PATTERN =
18
- /^#!\/usr\/bin\/env(?: -\S+)*(?: [^\s=-]+=\S+)* node(?: [^\r\n]+?)?\n/u
18
+
19
+ // -i -S
20
+ // -u name
21
+ // --ignore-environment
22
+ // --block-signal=SIGINT
23
+ const ENV_FLAGS = /^\s*-(-.*?\b|[ivS]+|[Pu](\s+|=)\S+)(?=\s|$)/
24
+
25
+ // NAME="some variable"
26
+ // FOO=bar
27
+ const ENV_VARS = /^\s*\w+=(?:"(?:[^"\\]|\\.)*"|\w+)/
28
+
29
+ /**
30
+ * @param {string} shebang
31
+ * @param {string} executableName
32
+ * @returns {boolean}
33
+ */
34
+ function isNodeShebang(shebang, executableName) {
35
+ if (shebang == null || shebang.length === 0) {
36
+ return false
37
+ }
38
+
39
+ shebang = shebang.slice(shebang.indexOf(ENV_SHEBANG) + ENV_SHEBANG.length)
40
+ while (ENV_FLAGS.test(shebang) || ENV_VARS.test(shebang)) {
41
+ shebang = shebang.replace(ENV_FLAGS, "").replace(ENV_VARS, "")
42
+ }
43
+
44
+ const [command] = shebang.trim().split(" ")
45
+ return command === executableName
46
+ }
47
+
48
+ /**
49
+ * @param {import('eslint').Rule.RuleContext} context The rule context.
50
+ * @returns {string}
51
+ */
52
+ function getExpectedExecutableName(context) {
53
+ const extension = path.extname(context.filename)
54
+ /** @type {{ executableMap: Record<string, string> }} */
55
+ const { executableMap = {} } = context.options?.[0] ?? {}
56
+
57
+ return executableMap[extension] ?? "node"
58
+ }
19
59
 
20
60
  /**
21
61
  * Gets the shebang line (includes a line ending) from a given code.
@@ -56,6 +96,16 @@ module.exports = {
56
96
  type: "array",
57
97
  items: { type: "string" },
58
98
  },
99
+ executableMap: {
100
+ type: "object",
101
+ patternProperties: {
102
+ "^\\.\\w+$": {
103
+ type: "string",
104
+ pattern: "^[\\w-]+$",
105
+ },
106
+ },
107
+ additionalProperties: false,
108
+ },
59
109
  },
60
110
  additionalProperties: false,
61
111
  },
@@ -64,7 +114,7 @@ module.exports = {
64
114
  unexpectedBOM: "This file must not have Unicode BOM.",
65
115
  expectedLF: "This file must have Unix linebreaks (LF).",
66
116
  expectedHashbangNode:
67
- 'This file needs shebang "#!/usr/bin/env node".',
117
+ 'This file needs shebang "#!/usr/bin/env {{executableName}}".',
68
118
  expectedHashbang: "This file needs no shebang.",
69
119
  },
70
120
  },
@@ -116,6 +166,7 @@ module.exports = {
116
166
  const needsShebang =
117
167
  isExecutable.ignored === true ||
118
168
  isBinFile(convertedAbsolutePath, packageJson?.bin, packageDirectory)
169
+ const executableName = getExpectedExecutableName(context)
119
170
  const info = getShebangInfo(sourceCode)
120
171
 
121
172
  return {
@@ -130,7 +181,7 @@ module.exports = {
130
181
 
131
182
  if (
132
183
  needsShebang
133
- ? NODE_SHEBANG_PATTERN.test(info.shebang)
184
+ ? isNodeShebang(info.shebang, executableName)
134
185
  : !info.shebang
135
186
  ) {
136
187
  // Good the shebang target.
@@ -159,10 +210,14 @@ module.exports = {
159
210
  context.report({
160
211
  loc,
161
212
  messageId: "expectedHashbangNode",
213
+ data: { executableName },
162
214
  fix(fixer) {
163
215
  return fixer.replaceTextRange(
164
216
  [-1, info.length],
165
- NODE_SHEBANG
217
+ NODE_SHEBANG.replaceAll(
218
+ "{{executableName}}",
219
+ executableName
220
+ )
166
221
  )
167
222
  },
168
223
  })
@@ -37,6 +37,7 @@ module.exports = {
37
37
  type: "object",
38
38
  properties: {
39
39
  version: getConfiguredNodeVersion.schema,
40
+ allowExperimental: { type: "boolean" },
40
41
  ignores: {
41
42
  type: "array",
42
43
  items: {
@@ -1,6 +1,6 @@
1
1
  "use strict"
2
2
 
3
- const { READ } = require("@eslint-community/eslint-utils")
3
+ const { CALL, CONSTRUCT, READ } = require("@eslint-community/eslint-utils")
4
4
 
5
5
  /** @type {import('../types.js').SupportVersionTraceMap} */
6
6
  const WebCrypto = {
@@ -106,8 +106,16 @@ const crypto = {
106
106
  [READ]: { supported: ["0.11.14"] },
107
107
  convertKey: { [READ]: { supported: ["10.0.0"] } },
108
108
  },
109
- Hash: { [READ]: { supported: ["0.1.92"] } },
110
- Hmac: { [READ]: { supported: ["0.1.94"] } },
109
+ Hash: {
110
+ [READ]: { supported: ["0.1.92"] },
111
+ [CALL]: { deprecated: ["22.0.0", "20.13.0"] },
112
+ [CONSTRUCT]: { deprecated: ["22.0.0", "20.13.0"] },
113
+ },
114
+ Hmac: {
115
+ [READ]: { supported: ["0.1.94"] },
116
+ [CALL]: { deprecated: ["22.0.0", "20.13.0"] },
117
+ [CONSTRUCT]: { deprecated: ["22.0.0", "20.13.0"] },
118
+ },
111
119
  KeyObject: {
112
120
  [READ]: { supported: ["11.6.0"] },
113
121
  from: { [READ]: { supported: ["15.0.0"] } },
@@ -36,7 +36,12 @@ const events = {
36
36
  supported: ["15.4.0"],
37
37
  },
38
38
  },
39
- CustomEvent: { [READ]: { experimental: ["18.7.0", "16.17.0"] } },
39
+ CustomEvent: {
40
+ [READ]: {
41
+ experimental: ["18.7.0", "16.17.0"],
42
+ supported: ["22.1.0", "20.13.0"],
43
+ },
44
+ },
40
45
  NodeEventTarget: {
41
46
  [READ]: {
42
47
  experimental: ["14.5.0"],
@@ -1,32 +1,34 @@
1
1
  "use strict"
2
2
 
3
- const { READ } = require("@eslint-community/eslint-utils")
3
+ const { READ, CALL, CONSTRUCT } = require("@eslint-community/eslint-utils")
4
4
 
5
5
  /** @type {import('../types.js').SupportVersionTraceMap} */
6
6
  const promises_api = {
7
- constants: { [READ]: { supported: ["18.4.0", "16.17.0"] } },
7
+ FileHandle: { [READ]: { supported: ["10.0.0"] } },
8
8
  access: { [READ]: { supported: ["10.0.0"] } },
9
9
  appendFile: { [READ]: { supported: ["10.0.0"] } },
10
10
  chmod: { [READ]: { supported: ["10.0.0"] } },
11
11
  chown: { [READ]: { supported: ["10.0.0"] } },
12
+ constants: { [READ]: { supported: ["18.4.0", "16.17.0"] } },
12
13
  copyFile: { [READ]: { supported: ["10.0.0"] } },
13
14
  cp: { [READ]: { experimental: ["16.7.0"] } },
15
+ glob: { [READ]: { experimental: ["22.0.0"] } },
14
16
  lchmod: { [READ]: { supported: ["10.0.0"], deprecated: ["10.0.0"] } },
15
17
  lchown: { [READ]: { supported: ["10.0.0"] } },
16
- lutimes: { [READ]: { supported: ["14.5.0", "12.19.0"] } },
17
18
  link: { [READ]: { supported: ["10.0.0"] } },
18
19
  lstat: { [READ]: { supported: ["10.0.0"] } },
20
+ lutimes: { [READ]: { supported: ["14.5.0", "12.19.0"] } },
19
21
  mkdir: { [READ]: { supported: ["10.0.0"] } },
20
22
  mkdtemp: { [READ]: { supported: ["10.0.0"] } },
21
23
  open: { [READ]: { supported: ["10.0.0"] } },
22
24
  opendir: { [READ]: { supported: ["12.12.0"] } },
23
- readdir: { [READ]: { supported: ["10.0.0"] } },
24
25
  readFile: { [READ]: { supported: ["10.0.0"] } },
26
+ readdir: { [READ]: { supported: ["10.0.0"] } },
25
27
  readlink: { [READ]: { supported: ["10.0.0"] } },
26
28
  realpath: { [READ]: { supported: ["10.0.0"] } },
27
29
  rename: { [READ]: { supported: ["10.0.0"] } },
28
- rmdir: { [READ]: { supported: ["10.0.0"] } },
29
30
  rm: { [READ]: { supported: ["14.14.0"] } },
31
+ rmdir: { [READ]: { supported: ["10.0.0"] } },
30
32
  stat: { [READ]: { supported: ["10.0.0"] } },
31
33
  statfs: { [READ]: { supported: ["19.6.0", "18.15.0"] } },
32
34
  symlink: { [READ]: { supported: ["10.0.0"] } },
@@ -35,7 +37,6 @@ const promises_api = {
35
37
  utimes: { [READ]: { supported: ["10.0.0"] } },
36
38
  watch: { [READ]: { supported: ["15.9.0", "14.18.0"] } },
37
39
  writeFile: { [READ]: { supported: ["10.0.0"] } },
38
- FileHandle: { [READ]: { supported: ["10.0.0"] } },
39
40
  }
40
41
 
41
42
  /** @type {import('../types.js').SupportVersionTraceMap} */
@@ -57,13 +58,15 @@ const callback_api = {
57
58
  fsync: { [READ]: { supported: ["0.1.96"] } },
58
59
  ftruncate: { [READ]: { supported: ["0.8.6"] } },
59
60
  futimes: { [READ]: { supported: ["0.4.2"] } },
61
+ glob: { [READ]: { experimental: ["22.0.0"] } },
60
62
  lchmod: { [READ]: { supported: ["0.1.8"], deprecated: ["0.4.7"] } },
61
63
  lchown: { [READ]: { supported: ["0.1.8"] } },
62
- lutimes: { [READ]: { supported: ["14.5.0", "12.19.0"] } },
63
64
  link: { [READ]: { supported: ["0.1.31"] } },
64
65
  lstat: { [READ]: { supported: ["0.1.30"] } },
66
+ lutimes: { [READ]: { supported: ["14.5.0", "12.19.0"] } },
65
67
  mkdir: { [READ]: { supported: ["0.1.8"] } },
66
68
  mkdtemp: { [READ]: { supported: ["5.10.0"] } },
69
+ native: { [READ]: { supported: ["9.2.0"] } },
67
70
  open: { [READ]: { supported: ["0.0.2"] } },
68
71
  openAsBlob: { [READ]: { experimental: ["19.8.0"] } },
69
72
  opendir: { [READ]: { supported: ["12.12.0"] } },
@@ -76,10 +79,9 @@ const callback_api = {
76
79
  [READ]: { supported: ["0.1.31"] },
77
80
  native: { [READ]: { supported: ["9.2.0"] } },
78
81
  },
79
- native: { [READ]: { supported: ["9.2.0"] } },
80
82
  rename: { [READ]: { supported: ["0.0.2"] } },
81
- rmdir: { [READ]: { supported: ["0.0.2"] } },
82
83
  rm: { [READ]: { supported: ["14.14.0"] } },
84
+ rmdir: { [READ]: { supported: ["0.0.2"] } },
83
85
  stat: { [READ]: { supported: ["0.0.2"] } },
84
86
  statfs: { [READ]: { supported: ["19.6.0", "18.15.0"] } },
85
87
  symlink: { [READ]: { supported: ["0.1.31"] } },
@@ -111,13 +113,15 @@ const synchronous_api = {
111
113
  fsyncSync: { [READ]: { supported: ["0.1.96"] } },
112
114
  ftruncateSync: { [READ]: { supported: ["0.8.6"] } },
113
115
  futimesSync: { [READ]: { supported: ["0.4.2"] } },
116
+ globSync: { [READ]: { experimental: ["22.0.0"] } },
114
117
  lchmodSync: { [READ]: { supported: ["0.1.8"], deprecated: ["0.4.7"] } },
115
118
  lchownSync: { [READ]: { supported: ["0.1.8"] } },
116
- lutimesSync: { [READ]: { supported: ["14.5.0", "12.19.0"] } },
117
119
  linkSync: { [READ]: { supported: ["0.1.31"] } },
118
120
  lstatSync: { [READ]: { supported: ["0.1.30"] } },
121
+ lutimesSync: { [READ]: { supported: ["14.5.0", "12.19.0"] } },
119
122
  mkdirSync: { [READ]: { supported: ["0.1.21"] } },
120
123
  mkdtempSync: { [READ]: { supported: ["5.10.0"] } },
124
+ native: { [READ]: { supported: ["9.2.0"] } },
121
125
  opendirSync: { [READ]: { supported: ["12.12.0"] } },
122
126
  openSync: { [READ]: { supported: ["0.1.21"] } },
123
127
  readdirSync: { [READ]: { supported: ["0.1.21"] } },
@@ -129,12 +133,11 @@ const synchronous_api = {
129
133
  [READ]: { supported: ["0.1.31"] },
130
134
  native: { [READ]: { supported: ["9.2.0"] } },
131
135
  },
132
- native: { [READ]: { supported: ["9.2.0"] } },
133
136
  renameSync: { [READ]: { supported: ["0.1.21"] } },
134
137
  rmdirSync: { [READ]: { supported: ["0.1.21"] } },
135
138
  rmSync: { [READ]: { supported: ["14.14.0"] } },
136
- statSync: { [READ]: { supported: ["0.1.21"] } },
137
139
  statfsSync: { [READ]: { supported: ["19.6.0", "18.15.0"] } },
140
+ statSync: { [READ]: { supported: ["0.1.21"] } },
138
141
  symlinkSync: { [READ]: { supported: ["0.1.31"] } },
139
142
  truncateSync: { [READ]: { supported: ["0.8.6"] } },
140
143
  unlinkSync: { [READ]: { supported: ["0.1.21"] } },
@@ -161,7 +164,11 @@ const fs = {
161
164
  FSWatcher: { [READ]: { supported: ["0.5.8"] } },
162
165
  StatWatcher: { [READ]: { supported: ["14.3.0", "12.20.0"] } },
163
166
  ReadStream: { [READ]: { supported: ["0.1.93"] } },
164
- Stats: { [READ]: { supported: ["0.1.21"] } },
167
+ Stats: {
168
+ [READ]: { supported: ["0.1.21"] },
169
+ [CALL]: { deprecated: ["22.0.0", "20.13.0"] },
170
+ [CONSTRUCT]: { deprecated: ["22.0.0", "20.13.0"] },
171
+ },
165
172
  StatFs: { [READ]: { supported: ["19.6.0", "18.15.0"] } },
166
173
  WriteStream: { [READ]: { supported: ["0.1.93"] } },
167
174
  common_objects: { [READ]: { supported: ["0.1.8"] } },
@@ -5,6 +5,7 @@ const { READ } = require("@eslint-community/eslint-utils")
5
5
  /** @type {import('../types.js').SupportVersionTraceMap} */
6
6
  const process = {
7
7
  allowedNodeEnvironmentFlags: { [READ]: { supported: ["10.10.0"] } },
8
+ availableMemory: { [READ]: { experimental: ["22.0.0", "20.13.0"] } },
8
9
  arch: { [READ]: { supported: ["0.5.0"] } },
9
10
  argv: { [READ]: { supported: ["0.1.27"] } },
10
11
  argv0: { [READ]: { supported: ["6.4.0"] } },
@@ -20,6 +20,12 @@ const test = {
20
20
  todo: { [READ]: { supported: ["20.2.0", "18.17.0"] } },
21
21
  only: { [READ]: { supported: ["20.2.0", "18.17.0"] } },
22
22
  },
23
+ suite: {
24
+ [READ]: { supported: ["22.0.0", "20.13.0"] },
25
+ skip: { [READ]: { supported: ["22.0.0", "20.13.0"] } },
26
+ todo: { [READ]: { supported: ["22.0.0", "20.13.0"] } },
27
+ only: { [READ]: { supported: ["22.0.0", "20.13.0"] } },
28
+ },
23
29
  before: { [READ]: { supported: ["18.8.0", "16.18.0"] } },
24
30
  after: { [READ]: { supported: ["18.8.0", "16.18.0"] } },
25
31
  beforeEach: { [READ]: { supported: ["18.8.0", "16.18.0"] } },
@@ -34,6 +34,7 @@ const v8 = {
34
34
  getHeapSnapshot: { [READ]: { supported: ["11.13.0"] } },
35
35
  getHeapSpaceStatistics: { [READ]: { supported: ["6.0.0"] } },
36
36
  getHeapStatistics: { [READ]: { supported: ["1.0.0"] } },
37
+ queryObjects: { [READ]: { experimental: ["22.0.0", "20.13.0"] } },
37
38
  setFlagsFromString: { [READ]: { supported: ["1.0.0"] } },
38
39
  stopCoverage: { [READ]: { supported: ["15.1.0", "14.18.0", "12.22.0"] } },
39
40
  takeCoverage: { [READ]: { supported: ["15.1.0", "14.18.0", "12.22.0"] } },
@@ -15,51 +15,56 @@ const semverRangeSubset = require("semver/ranges/subset")
15
15
  * Parses the options.
16
16
  * @param {import('eslint').Rule.RuleContext} context The rule context.
17
17
  * @returns {Readonly<{
18
- * version: import('semver').Range,
19
- * ignores: Set<string>
18
+ * version: import('semver').Range;
19
+ * ignores: Set<string>;
20
+ * allowExperimental: boolean;
20
21
  * }>} Parsed value.
21
22
  */
22
23
  function parseOptions(context) {
23
24
  const raw = context.options[0] || {}
24
25
  const version = getConfiguredNodeVersion(context)
25
26
  const ignores = new Set(raw.ignores || [])
27
+ const allowExperimental = raw.allowExperimental ?? false
26
28
 
27
- return Object.freeze({ version, ignores })
29
+ return Object.freeze({ version, ignores, allowExperimental })
28
30
  }
29
31
 
30
32
  /**
31
33
  * Check if it has been supported.
32
- * @param {import('../unsupported-features/types.js').SupportInfo} info The support info.
33
- * @param {import('semver').Range} configured The configured version range.
34
+ * @param {string[] | undefined} featureRange The target features supported range
35
+ * @param {import('semver').Range} requestedRange The configured version range.
36
+ * @returns {boolean}
34
37
  */
35
- function isSupported({ supported }, configured) {
36
- if (supported == null || supported.length === 0) {
38
+ function isInRange(featureRange, requestedRange) {
39
+ if (featureRange == null || featureRange.length === 0) {
37
40
  return false
38
41
  }
39
42
 
40
- const [latest] = rsort(supported)
43
+ const [latest] = rsort(featureRange)
41
44
  const range = getSemverRange(
42
- [...supported.map(version => `^${version}`), `>= ${latest}`].join("||")
45
+ [...featureRange.map(version => `^${version}`), `>= ${latest}`].join(
46
+ "||"
47
+ )
43
48
  )
44
49
 
45
50
  if (range == null) {
46
51
  return false
47
52
  }
48
53
 
49
- return semverRangeSubset(configured, range)
54
+ return semverRangeSubset(requestedRange, range)
50
55
  }
51
56
 
52
57
  /**
53
58
  * Get the formatted text of a given supported version.
54
- * @param {import('../unsupported-features/types.js').SupportInfo} info The support info.
59
+ * @param {string[] | undefined} versions The support info.
55
60
  * @returns {string | undefined}
56
61
  */
57
- function supportedVersionToString({ supported }) {
58
- if (supported == null || supported.length === 0) {
62
+ function versionsToString(versions) {
63
+ if (versions == null) {
59
64
  return
60
65
  }
61
66
 
62
- const [latest, ...backported] = rsort(supported)
67
+ const [latest, ...backported] = rsort(versions)
63
68
 
64
69
  if (backported.length === 0) {
65
70
  return latest
@@ -92,20 +97,54 @@ module.exports.checkUnsupportedBuiltins = function checkUnsupportedBuiltins(
92
97
 
93
98
  for (const { node, path, info } of references) {
94
99
  const name = unprefixNodeColon(path.join("."))
95
- const supported = isSupported(info, options.version)
96
100
 
97
- if (supported === true || options.ignores.has(name)) {
101
+ if (options.ignores.has(name)) {
98
102
  continue
99
103
  }
100
- const supportedVersion = supportedVersionToString(info)
104
+
105
+ if (options.allowExperimental) {
106
+ if (isInRange(info.experimental, options.version)) {
107
+ continue
108
+ }
109
+
110
+ const experimentalVersion = versionsToString(info.experimental)
111
+ if (experimentalVersion) {
112
+ context.report({
113
+ node,
114
+ messageId: "not-experimental-till",
115
+ data: {
116
+ name: name,
117
+ experimental: experimentalVersion,
118
+ version: options.version.raw,
119
+ },
120
+ })
121
+ continue
122
+ }
123
+ }
124
+
125
+ if (isInRange(info.supported, options.version)) {
126
+ continue
127
+ }
128
+
129
+ const supportedVersion = versionsToString(info.supported)
130
+ if (supportedVersion) {
131
+ context.report({
132
+ node,
133
+ messageId: "not-supported-till",
134
+ data: {
135
+ name: name,
136
+ supported: supportedVersion,
137
+ version: options.version.raw,
138
+ },
139
+ })
140
+ continue
141
+ }
142
+
101
143
  context.report({
102
144
  node,
103
- messageId: supportedVersion
104
- ? "not-supported-till"
105
- : "not-supported-yet",
145
+ messageId: "not-supported-yet",
106
146
  data: {
107
- name: path.join("."),
108
- supported: /** @type string */ (supportedVersion),
147
+ name: name,
109
148
  version: options.version.raw,
110
149
  },
111
150
  })
@@ -113,6 +152,11 @@ module.exports.checkUnsupportedBuiltins = function checkUnsupportedBuiltins(
113
152
  }
114
153
 
115
154
  exports.messages = {
155
+ "not-experimental-till": [
156
+ "The '{{name}}' is not an experimental feature",
157
+ "until Node.js {{experimental}}.",
158
+ "The configured version range is '{{version}}'.",
159
+ ].join(" "),
116
160
  "not-supported-till": [
117
161
  "The '{{name}}' is still an experimental feature",
118
162
  "and is not supported until Node.js {{supported}}.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-n",
3
- "version": "17.5.1",
3
+ "version": "17.7.0",
4
4
  "description": "Additional ESLint's rules for Node.js",
5
5
  "engines": {
6
6
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"