eslint-plugin-n 17.15.1 → 17.16.2

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.
@@ -3,6 +3,7 @@
3
3
  * See LICENSE file in root directory for full license.
4
4
  */
5
5
  "use strict"
6
+ const { getSourceCode } = require("../util/eslint-compat")
6
7
 
7
8
  /** @type {import('eslint').Rule.RuleModule} */
8
9
  module.exports = {
@@ -27,7 +28,7 @@ module.exports = {
27
28
 
28
29
  create(context) {
29
30
  const callbacks = context.options[0] || ["callback", "cb", "next"]
30
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
31
+ const sourceCode = getSourceCode(context)
31
32
 
32
33
  /**
33
34
  * Find the closest parent matching a list of types.
@@ -5,6 +5,7 @@
5
5
  "use strict"
6
6
 
7
7
  const { hasParentNode } = require("../util/has-parent-node.js")
8
+ const { getSourceCode, getScope } = require("../util/eslint-compat")
8
9
 
9
10
  /*istanbul ignore next */
10
11
  /**
@@ -302,7 +303,7 @@ module.exports = {
302
303
  const batchAssignAllowed = Boolean(
303
304
  context.options[1] != null && context.options[1].allowBatchAssign
304
305
  )
305
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
306
+ const sourceCode = getSourceCode(context)
306
307
 
307
308
  /**
308
309
  * Gets the location info of reports.
@@ -426,8 +427,8 @@ module.exports = {
426
427
  }
427
428
 
428
429
  return {
429
- "Program:exit"(node) {
430
- const scope = sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9
430
+ "Program:exit"() {
431
+ const scope = getScope(context)
431
432
 
432
433
  switch (mode) {
433
434
  case "module.exports":
@@ -4,6 +4,8 @@
4
4
  */
5
5
  "use strict"
6
6
 
7
+ const { getScope, getAncestors } = require("../util/eslint-compat")
8
+
7
9
  const ACCEPTABLE_PARENTS = [
8
10
  "AssignmentExpression",
9
11
  "VariableDeclarator",
@@ -59,26 +61,18 @@ module.exports = {
59
61
  },
60
62
 
61
63
  create(context) {
62
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
63
-
64
64
  return {
65
65
  CallExpression(node) {
66
- const currentScope =
67
- sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9
66
+ const currentScope = getScope(context, node)
68
67
 
69
68
  if (
70
69
  node.callee.type === "Identifier" &&
71
70
  node.callee.name === "require" &&
72
71
  !isShadowed(currentScope, node.callee)
73
72
  ) {
74
- const isGoodRequire = (
75
- sourceCode.getAncestors?.(node) ??
76
- context.getAncestors()
77
- ) // TODO: remove context.getAncestors() when dropping support for ESLint < v9
78
- .every(
79
- parent =>
80
- ACCEPTABLE_PARENTS.indexOf(parent.type) > -1
81
- )
73
+ const isGoodRequire = getAncestors(context, node).every(
74
+ parent => ACCEPTABLE_PARENTS.indexOf(parent.type) > -1
75
+ )
82
76
 
83
77
  if (!isGoodRequire) {
84
78
  context.report({ node, messageId: "unexpected" })
@@ -4,6 +4,8 @@
4
4
  */
5
5
  "use strict"
6
6
 
7
+ const { getScope } = require("../util/eslint-compat")
8
+
7
9
  /** @type {import('eslint').Rule.RuleModule} */
8
10
  module.exports = {
9
11
  meta: {
@@ -25,7 +27,6 @@ module.exports = {
25
27
  },
26
28
 
27
29
  create(context) {
28
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
29
30
  const errorArgument = context.options[0] || "err"
30
31
 
31
32
  /**
@@ -71,7 +72,7 @@ module.exports = {
71
72
  * @returns {void}
72
73
  */
73
74
  function checkForError(node) {
74
- const scope = sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9
75
+ const scope = getScope(context, node)
75
76
  const parameters = getParameters(scope)
76
77
  const firstParameter = parameters[0]
77
78
 
@@ -11,6 +11,7 @@ const getConvertPath = require("../util/get-convert-path")
11
11
  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
+ const { getSourceCode } = require("../util/eslint-compat")
14
15
 
15
16
  const ENV_SHEBANG = "#!/usr/bin/env"
16
17
  const NODE_SHEBANG = `${ENV_SHEBANG} {{executableName}}\n`
@@ -51,8 +52,9 @@ function isNodeShebang(shebang, executableName) {
51
52
  */
52
53
  function getExpectedExecutableName(context) {
53
54
  const extension = path.extname(context.filename ?? context.getFilename())
54
- /** @type {{ executableMap: Record<string, string> }} */
55
- const { executableMap = {} } = context.options?.[0] ?? {}
55
+ const { executableMap = {} } =
56
+ /** @type {[{ executableMap: Record<string, string> }]} */
57
+ (context.options)?.[0] ?? {}
56
58
 
57
59
  return executableMap[extension] ?? "node"
58
60
  }
@@ -119,7 +121,7 @@ module.exports = {
119
121
  },
120
122
  },
121
123
  create(context) {
122
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
124
+ const sourceCode = getSourceCode(context)
123
125
  const filePath = context.filename ?? context.getFilename()
124
126
  if (filePath === "<input>") {
125
127
  return {}
@@ -15,6 +15,7 @@ const getConfiguredNodeVersion = require("../util/get-configured-node-version")
15
15
  const getSemverRange = require("../util/get-semver-range")
16
16
  const extendTrackmapWithNodePrefix = require("../util/extend-trackmap-with-node-prefix")
17
17
  const unprefixNodeColon = require("../util/unprefix-node-colon")
18
+ const { getScope } = require("../util/eslint-compat")
18
19
 
19
20
  /** @typedef {import('../unsupported-features/types.js').DeprecatedInfo} DeprecatedInfo */
20
21
  /**
@@ -737,7 +738,10 @@ function toName(type, path) {
737
738
  * @returns {ParsedOptions} Parsed options
738
739
  */
739
740
  function parseOptions(context) {
740
- const raw = context.options[0] || {}
741
+ const raw = /** @type {{
742
+ ignoreModuleItems?: string[];
743
+ ignoreGlobalItems?: string[];
744
+ }} */ (context.options[0] || {})
741
745
  const version = getConfiguredNodeVersion(context)
742
746
  const ignoredModuleItems = new Set(raw.ignoreModuleItems || [])
743
747
  const ignoredGlobalItems = new Set(raw.ignoreGlobalItems || [])
@@ -820,10 +824,9 @@ module.exports = {
820
824
  })
821
825
  }
822
826
 
823
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
824
827
  return {
825
- "Program:exit"(node) {
826
- const scope = sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9
828
+ "Program:exit"() {
829
+ const scope = getScope(context)
827
830
 
828
831
  const tracker = new ReferenceTracker(scope, {
829
832
  mode: "legacy",
@@ -5,6 +5,7 @@
5
5
  "use strict"
6
6
 
7
7
  const { findVariable } = require("@eslint-community/eslint-utils")
8
+ const { getScope } = require("../util/eslint-compat")
8
9
 
9
10
  /**
10
11
  * @param {import('estree').Node} node
@@ -61,11 +62,9 @@ module.exports = {
61
62
  type: "problem",
62
63
  },
63
64
  create(context) {
64
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
65
-
66
65
  return {
67
66
  AssignmentExpression(node) {
68
- const scope = sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9
67
+ const scope = getScope(context)
69
68
 
70
69
  if (
71
70
  !isExports(node.left, scope) ||
@@ -11,6 +11,7 @@ const {
11
11
  getStringIfConstant,
12
12
  } = require("@eslint-community/eslint-utils")
13
13
  const { hasParentNode } = require("../util/has-parent-node.js")
14
+ const { getSourceCode } = require("../util/eslint-compat")
14
15
 
15
16
  /**
16
17
  * Get the first char of the specified template element.
@@ -195,7 +196,7 @@ module.exports = {
195
196
  create(context) {
196
197
  return {
197
198
  "Program:exit"(programNode) {
198
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
199
+ const sourceCode = getSourceCode(context)
199
200
  const globalScope =
200
201
  sourceCode.getScope?.(programNode) ?? context.getScope()
201
202
  const tracker = new ReferenceTracker(globalScope)
@@ -10,6 +10,7 @@ const rangeSubset = require("semver/ranges/subset")
10
10
  const getConfiguredNodeVersion = require("../../util/get-configured-node-version")
11
11
  const getSemverRange = require("../../util/get-semver-range")
12
12
  const mergeVisitorsInPlace = require("../../util/merge-visitors-in-place")
13
+ const { getScope } = require("../../util/eslint-compat")
13
14
  /** @type {Record<string, ESSyntax>} */
14
15
  const features = require("./es-syntax.json")
15
16
 
@@ -113,8 +114,7 @@ function normalizeScope(initialScope, node) {
113
114
  * @returns {boolean}
114
115
  */
115
116
  function isStrict(context, node) {
116
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
117
- const scope = sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9
117
+ const scope = getScope(context)
118
118
  return normalizeScope(scope, node).isStrict
119
119
  }
120
120
 
@@ -4,12 +4,13 @@
4
4
  */
5
5
  "use strict"
6
6
 
7
+ const { getStringIfConstant } = require("@eslint-community/eslint-utils")
8
+
7
9
  const { Range } = require("semver")
8
10
 
9
11
  const getConfiguredNodeVersion = require("../util/get-configured-node-version")
10
- const visitImport = require("../util/visit-import")
11
- const visitRequire = require("../util/visit-require")
12
- const mergeVisitorsInPlace = require("../util/merge-visitors-in-place")
12
+ const stripImportPathParams = require("../util/strip-import-path-params")
13
+
13
14
  const {
14
15
  NodeBuiltinModules,
15
16
  } = require("../unsupported-features/node-builtins.js")
@@ -27,6 +28,104 @@ const messageId = "preferNodeProtocol"
27
28
  const supportedRangeForEsm = new Range("^12.20.0 || >= 14.13.1")
28
29
  const supportedRangeForCjs = new Range("^14.18.0 || >= 16.0.0")
29
30
 
31
+ /**
32
+ * @param {import('estree').Node} [node]
33
+ * @returns {node is import('estree').Literal}
34
+ */
35
+ function isStringLiteral(node) {
36
+ return node?.type === "Literal" && typeof node.type === "string"
37
+ }
38
+
39
+ /**
40
+ * @param {import('eslint').Rule.RuleContext} context
41
+ * @param {import('../util/import-target.js').ModuleStyle} moduleStyle
42
+ * @returns {boolean}
43
+ */
44
+ function isEnablingThisRule(context, moduleStyle) {
45
+ const version = getConfiguredNodeVersion(context)
46
+
47
+ // Only check Node.js version because this rule is meaningless if configured Node.js version doesn't match semver range.
48
+ if (!version.intersects(supportedRangeForEsm)) {
49
+ return false
50
+ }
51
+
52
+ // Only check when using `require`
53
+ if (
54
+ moduleStyle === "require" &&
55
+ !version.intersects(supportedRangeForCjs)
56
+ ) {
57
+ return false
58
+ }
59
+
60
+ return true
61
+ }
62
+
63
+ /**
64
+ * @param {import('estree').Node} node
65
+ * @returns {boolean}
66
+ **/
67
+ function isValidRequireArgument(node) {
68
+ const rawName = getStringIfConstant(node)
69
+ if (typeof rawName !== "string") {
70
+ return false
71
+ }
72
+
73
+ const name = stripImportPathParams(rawName)
74
+ if (!isBuiltin(name)) {
75
+ return false
76
+ }
77
+
78
+ return true
79
+ }
80
+
81
+ /**
82
+ * @param {import('estree').Node | null | undefined} node
83
+ * @param {import('eslint').Rule.RuleContext} context
84
+ * @param {import('../util/import-target.js').ModuleStyle} moduleStyle
85
+ */
86
+ function validate(node, context, moduleStyle) {
87
+ if (node == null) {
88
+ return
89
+ }
90
+
91
+ if (!isEnablingThisRule(context, moduleStyle)) {
92
+ return
93
+ }
94
+
95
+ if (!isStringLiteral(node)) {
96
+ return
97
+ }
98
+
99
+ if (moduleStyle === "require" && !isValidRequireArgument(node)) {
100
+ return
101
+ }
102
+
103
+ if (
104
+ !("value" in node) ||
105
+ typeof node.value !== "string" ||
106
+ node.value.startsWith("node:") ||
107
+ !isBuiltin(node.value) ||
108
+ !isBuiltin(`node:${node.value}`)
109
+ ) {
110
+ return
111
+ }
112
+
113
+ context.report({
114
+ node,
115
+ messageId,
116
+ data: {
117
+ moduleName: node.value,
118
+ },
119
+ fix(fixer) {
120
+ const firstCharacterIndex = (node?.range?.[0] ?? 0) + 1
121
+ return fixer.replaceTextRange(
122
+ [firstCharacterIndex, firstCharacterIndex],
123
+ "node:"
124
+ )
125
+ },
126
+ })
127
+ }
128
+
30
129
  /** @type {import('eslint').Rule.RuleModule} */
31
130
  module.exports = {
32
131
  meta: {
@@ -52,139 +151,36 @@ module.exports = {
52
151
  type: "suggestion",
53
152
  },
54
153
  create(context) {
55
- /**
56
- * @param {import('estree').Node} node
57
- * @param {object} options
58
- * @param {string} options.name
59
- * @param {number} options.argumentsLength
60
- * @returns {node is import('estree').CallExpression}
61
- */
62
- function isCallExpression(node, { name, argumentsLength }) {
63
- if (node?.type !== "CallExpression") {
64
- return false
65
- }
66
-
67
- if (node.optional) {
68
- return false
69
- }
70
-
71
- if (node.arguments.length !== argumentsLength) {
72
- return false
73
- }
74
-
75
- if (
76
- node.callee.type !== "Identifier" ||
77
- node.callee.name !== name
78
- ) {
79
- return false
80
- }
81
-
82
- return true
83
- }
84
-
85
- /**
86
- * @param {import('estree').Node} [node]
87
- * @returns {node is import('estree').Literal}
88
- */
89
- function isStringLiteral(node) {
90
- return node?.type === "Literal" && typeof node.type === "string"
91
- }
92
-
93
- /**
94
- * @param {import('estree').Node | undefined} node
95
- * @returns {node is import('estree').CallExpression}
96
- */
97
- function isStaticRequire(node) {
98
- return (
99
- node != null &&
100
- isCallExpression(node, {
101
- name: "require",
102
- argumentsLength: 1,
103
- }) &&
104
- isStringLiteral(node.arguments[0])
105
- )
106
- }
107
-
108
- /**
109
- * @param {import('eslint').Rule.RuleContext} context
110
- * @param {import('../util/import-target.js').ModuleStyle} moduleStyle
111
- * @returns {boolean}
112
- */
113
- function isEnablingThisRule(context, moduleStyle) {
114
- const version = getConfiguredNodeVersion(context)
115
-
116
- // Only check Node.js version because this rule is meaningless if configured Node.js version doesn't match semver range.
117
- if (!version.intersects(supportedRangeForEsm)) {
118
- return false
119
- }
120
-
121
- // Only check when using `require`
122
- if (
123
- moduleStyle === "require" &&
124
- !version.intersects(supportedRangeForCjs)
125
- ) {
126
- return false
127
- }
128
-
129
- return true
130
- }
154
+ return {
155
+ CallExpression(node) {
156
+ if (node.type !== "CallExpression") {
157
+ return
158
+ }
159
+
160
+ if (
161
+ node.optional ||
162
+ node.arguments.length !== 1 ||
163
+ node.callee.type !== "Identifier" ||
164
+ node.callee.name !== "require"
165
+ ) {
166
+ return
167
+ }
168
+
169
+ return validate(node.arguments[0], context, "require")
170
+ },
131
171
 
132
- /** @type {import('../util/import-target.js')[]} */
133
- const targets = []
134
- return [
135
- visitImport(context, { includeCore: true }, importTargets => {
136
- targets.push(...importTargets)
137
- }),
138
- visitRequire(context, { includeCore: true }, requireTargets => {
139
- targets.push(
140
- ...requireTargets.filter(target =>
141
- isStaticRequire(target.node.parent)
142
- )
143
- )
144
- }),
145
- {
146
- "Program:exit"() {
147
- for (const { node, moduleStyle } of targets) {
148
- if (!isEnablingThisRule(context, moduleStyle)) {
149
- continue
150
- }
151
-
152
- if (node.type === "TemplateLiteral") {
153
- continue
154
- }
155
-
156
- if (
157
- !("value" in node) ||
158
- typeof node.value !== "string" ||
159
- node.value.startsWith("node:") ||
160
- !isBuiltin(node.value) ||
161
- !isBuiltin(`node:${node.value}`)
162
- ) {
163
- continue
164
- }
165
-
166
- context.report({
167
- node,
168
- messageId,
169
- data: {
170
- moduleName: node.value,
171
- },
172
- fix(fixer) {
173
- const firstCharacterIndex =
174
- (node?.range?.[0] ?? 0) + 1
175
- return fixer.replaceTextRange(
176
- [firstCharacterIndex, firstCharacterIndex],
177
- "node:"
178
- )
179
- },
180
- })
181
- }
182
- },
172
+ ExportAllDeclaration(node) {
173
+ return validate(node.source, context, "import")
183
174
  },
184
- ].reduce(
185
- (mergedVisitor, thisVisitor) =>
186
- mergeVisitorsInPlace(mergedVisitor, thisVisitor),
187
- {}
188
- )
175
+ ExportNamedDeclaration(node) {
176
+ return validate(node.source, context, "import")
177
+ },
178
+ ImportDeclaration(node) {
179
+ return validate(node.source, context, "import")
180
+ },
181
+ ImportExpression(node) {
182
+ return validate(node.source, context, "import")
183
+ },
184
+ }
189
185
  },
190
186
  }
@@ -9,6 +9,7 @@ const {
9
9
  CONSTRUCT,
10
10
  ReferenceTracker,
11
11
  } = require("@eslint-community/eslint-utils")
12
+ const { getScope } = require("../../util/eslint-compat")
12
13
 
13
14
  /** @type {import('@eslint-community/eslint-utils').TraceMap<boolean>} */
14
15
  const dns = {
@@ -57,9 +58,8 @@ module.exports = {
57
58
 
58
59
  create(context) {
59
60
  return {
60
- "Program:exit"(node) {
61
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
62
- const scope = sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9
61
+ "Program:exit"() {
62
+ const scope = getScope(context)
63
63
  const tracker = new ReferenceTracker(scope, { mode: "legacy" })
64
64
  const references = [
65
65
  ...tracker.iterateCjsReferences(traceMap),
@@ -5,6 +5,7 @@
5
5
  "use strict"
6
6
 
7
7
  const { CALL, ReferenceTracker } = require("@eslint-community/eslint-utils")
8
+ const { getScope } = require("../../util/eslint-compat")
8
9
 
9
10
  /** @type {import('@eslint-community/eslint-utils').TraceMap<boolean>} */
10
11
  const traceMap = {
@@ -55,9 +56,8 @@ module.exports = {
55
56
 
56
57
  create(context) {
57
58
  return {
58
- "Program:exit"(node) {
59
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
60
- const scope = sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9
59
+ "Program:exit"() {
60
+ const scope = getScope(context)
61
61
  const tracker = new ReferenceTracker(scope, { mode: "legacy" })
62
62
  const references = [
63
63
  ...tracker.iterateCjsReferences(traceMap),
@@ -5,7 +5,7 @@
5
5
  "use strict"
6
6
 
7
7
  const { ReferenceTracker } = require("@eslint-community/eslint-utils")
8
-
8
+ const { getScope } = require("../util/eslint-compat")
9
9
  /**
10
10
  * @typedef TraceMap
11
11
  * @property {import('@eslint-community/eslint-utils').TraceMap<boolean>} globals
@@ -36,9 +36,7 @@ class Verifier {
36
36
  */
37
37
  verifyToPreferGlobals() {
38
38
  const { context, traceMap } = this
39
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
40
- const scope =
41
- sourceCode.getScope?.(sourceCode.ast) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9
39
+ const scope = getScope(context)
42
40
  const tracker = new ReferenceTracker(scope, {
43
41
  mode: "legacy",
44
42
  })
@@ -57,9 +55,7 @@ class Verifier {
57
55
  */
58
56
  verifyToPreferModules() {
59
57
  const { context, traceMap } = this
60
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
61
- const scope =
62
- sourceCode.getScope?.(sourceCode.ast) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9
58
+ const scope = getScope(context)
63
59
  const tracker = new ReferenceTracker(scope)
64
60
 
65
61
  for (const { node } of tracker.iterateGlobalReferences(
@@ -96,7 +96,9 @@ function createRestrictions(defs) {
96
96
  * @returns {void}
97
97
  */
98
98
  exports.checkForRestriction = function checkForRestriction(context, targets) {
99
- const restrictions = createRestrictions(context.options[0])
99
+ const restrictions = createRestrictions(
100
+ /** @type {string[]} */ (context.options[0])
101
+ )
100
102
 
101
103
  for (const target of targets) {
102
104
  const restriction = restrictions.find(r => r.match(target))
@@ -10,6 +10,7 @@ const getConfiguredNodeVersion = require("./get-configured-node-version")
10
10
  const getSemverRange = require("./get-semver-range")
11
11
  const unprefixNodeColon = require("./unprefix-node-colon")
12
12
  const semverRangeSubset = require("semver/ranges/subset")
13
+ const { getScope } = require("../util/eslint-compat")
13
14
 
14
15
  /**
15
16
  * Parses the options.
@@ -21,7 +22,10 @@ const semverRangeSubset = require("semver/ranges/subset")
21
22
  * }>} Parsed value.
22
23
  */
23
24
  function parseOptions(context) {
24
- const raw = context.options[0] || {}
25
+ const raw = /** @type {{
26
+ * ignores?: string[];
27
+ * allowExperimental?: boolean;
28
+ * }} */ (context.options[0] || {})
25
29
  const version = getConfiguredNodeVersion(context)
26
30
  const ignores = new Set(raw.ignores || [])
27
31
  const allowExperimental = raw.allowExperimental ?? false
@@ -86,8 +90,7 @@ module.exports.checkUnsupportedBuiltins = function checkUnsupportedBuiltins(
86
90
  traceMap
87
91
  ) {
88
92
  const options = parseOptions(context)
89
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
90
- const scope = sourceCode.getScope?.(sourceCode.ast) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9
93
+ const scope = getScope(context)
91
94
  const tracker = new ReferenceTracker(scope, { mode: "legacy" })
92
95
  const references = [
93
96
  ...tracker.iterateCjsReferences(traceMap.modules ?? {}),
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @fileoverview Utilities for eslint compatibility.
3
+ * @see https://eslint.org/docs/latest/use/migrate-to-9.0.0#removed-context-methods
4
+ * @author aladdin-add<weiran.zsd@outlook.com>
5
+ */
6
+ "use strict"
7
+
8
+ /** @import { Rule } from 'eslint' */
9
+ /** @typedef {import('estree').Node} Node */
10
+
11
+ exports.getSourceCode = function (/** @type Rule.RuleContext */ context) {
12
+ return context.sourceCode || context.getSourceCode()
13
+ }
14
+
15
+ exports.getScope = function (
16
+ /** @type {Rule.RuleContext} */ context,
17
+ /** @type {Node} */ node
18
+ ) {
19
+ const sourceCode = exports.getSourceCode(context)
20
+ return sourceCode.getScope?.(node || sourceCode.ast) || context.getScope()
21
+ }
22
+
23
+ exports.getAncestors = function (
24
+ /** @type {Rule.RuleContext} */ context,
25
+ /** @type {Node} */ node
26
+ ) {
27
+ const sourceCode = exports.getSourceCode(context)
28
+ return sourceCode.getAncestors?.(node) || context.getAncestors()
29
+ }
30
+
31
+ exports.getCwd = function (/** @type {Rule.RuleContext} */ context) {
32
+ return context.cwd || context.getCwd()
33
+ }
34
+
35
+ exports.getPhysicalFilename = function (
36
+ /** @type {Rule.RuleContext} */ context
37
+ ) {
38
+ return context.physicalFilename || context.getPhysicalFilename?.()
39
+ }
40
+
41
+ exports.getFilename = function (/** @type {Rule.RuleContext} */ context) {
42
+ return context.filename || context.getFilename?.()
43
+ }
@@ -9,10 +9,14 @@
9
9
  */
10
10
  const DEFAULT_VALUE = []
11
11
 
12
+ /**
13
+ * @typedef {{allowModules:? string[]}|undefined} Option
14
+ */
15
+
12
16
  /**
13
17
  * Gets `allowModules` property from a given option object.
14
18
  *
15
- * @param {{allowModules:? string[]}|undefined} option - An option object to get.
19
+ * @param {Option} option - An option object to get.
16
20
  * @returns {string[]|null} The `allowModules` value, or `null`.
17
21
  */
18
22
  function get(option) {
@@ -34,9 +38,9 @@ function get(option) {
34
38
  */
35
39
  module.exports = function getAllowModules(context) {
36
40
  return (
37
- get(context.options[0]) ??
38
- get(context.settings?.n) ??
39
- get(context.settings?.node) ??
41
+ get(/** @type {Option} */ (context.options[0])) ??
42
+ get(/** @type {Option} */ (context.settings?.n)) ??
43
+ get(/** @type {Option} */ (context.settings?.node)) ??
40
44
  DEFAULT_VALUE
41
45
  )
42
46
  }
@@ -10,10 +10,14 @@ const getSemverRange = require("./get-semver-range")
10
10
 
11
11
  const fallbackRange = new Range(">=16.0.0")
12
12
 
13
+ /**
14
+ * @typedef {{ version:? string } | undefined} VersionOption
15
+ */
16
+
13
17
  /**
14
18
  * Gets `version` property from a given option object.
15
19
  *
16
- * @param {Record<string, string>|undefined} option - An option object to get.
20
+ * @param {VersionOption} option - An option object to get.
17
21
  * @returns {import("semver").Range|undefined} The `allowModules` value, or `null`.
18
22
  */
19
23
  function getVersionRange(option) {
@@ -55,9 +59,11 @@ function getEnginesNode(context) {
55
59
  */
56
60
  module.exports = function getConfiguredNodeVersion(context) {
57
61
  return (
58
- getVersionRange(context.options?.[0]) ??
59
- getVersionRange(context.settings?.n) ??
60
- getVersionRange(context.settings?.node) ??
62
+ getVersionRange(/** @type {VersionOption} */ (context.options?.[0])) ??
63
+ getVersionRange(/** @type {VersionOption} */ (context.settings?.n)) ??
64
+ getVersionRange(
65
+ /** @type {VersionOption} */ (context.settings?.node)
66
+ ) ??
61
67
  getEnginesNode(context) ??
62
68
  fallbackRange
63
69
  )
@@ -111,10 +111,14 @@ function combine(converters) {
111
111
  }
112
112
  }
113
113
 
114
+ /**
115
+ * @typedef {{ convertPath?: ConvertPath } | undefined} ConvertPathOption
116
+ */
117
+
114
118
  /**
115
119
  * Parses `convertPath` property from a given option object.
116
120
  *
117
- * @param {{convertPath?: ConvertPath}|undefined} option - An option object to get.
121
+ * @param {ConvertPathOption} option - An option object to get.
118
122
  * @returns {Converter['convert']|null} A function which converts a path., or `null`.
119
123
  */
120
124
  function parse(option) {
@@ -148,9 +152,9 @@ function parse(option) {
148
152
  */
149
153
  module.exports = function getConvertPath(context) {
150
154
  return (
151
- parse(context.options?.[0]) ??
152
- parse(context.settings?.n) ??
153
- parse(context.settings?.node) ??
155
+ parse(/** @type {ConvertPathOption} */ (context.options?.[0])) ??
156
+ parse(/** @type {ConvertPathOption} */ (context.settings?.n)) ??
157
+ parse(/** @type {ConvertPathOption} */ (context.settings?.node)) ??
154
158
  identity
155
159
  )
156
160
  }
@@ -10,11 +10,16 @@ const DEFAULT_VALUE = []
10
10
  /**
11
11
  * Gets `resolvePaths` property from a given option object.
12
12
  *
13
- * @param {{ resolvePaths: unknown[] } | undefined} option - An option object to get.
13
+ * @param {unknown} option - An option object to get.
14
14
  * @returns {string[] | undefined} The `allowModules` value, or `null`.
15
15
  */
16
16
  function get(option) {
17
- if (Array.isArray(option?.resolvePaths)) {
17
+ if (
18
+ option != null &&
19
+ typeof option === "object" &&
20
+ "resolvePaths" in option &&
21
+ Array.isArray(option?.resolvePaths)
22
+ ) {
18
23
  return option.resolvePaths.map(String)
19
24
  }
20
25
  }
@@ -11,10 +11,14 @@
11
11
  /** @type {ResolverConfig} */
12
12
  const DEFAULT_VALUE = {}
13
13
 
14
+ /**
15
+ * @typedef {{ resolverConfig?: ResolverConfig } | undefined} ResolverConfigOption
16
+ */
17
+
14
18
  /**
15
19
  * Gets `resolverConfig` property from a given option object.
16
20
  *
17
- * @param {{ resolverConfig: ResolverConfig } | undefined} option - An option object to get.
21
+ * @param {ResolverConfigOption} option - An option object to get.
18
22
  * @returns {ResolverConfig | undefined} The `allowModules` value, or `null`.
19
23
  */
20
24
  function get(option) {
@@ -33,9 +37,11 @@ function get(option) {
33
37
  */
34
38
  module.exports = function getResolverConfig(context, optionIndex = 0) {
35
39
  return (
36
- get(context.options?.[optionIndex]) ??
37
- get(context.settings?.n) ??
38
- get(context.settings?.node) ??
40
+ get(
41
+ /** @type {ResolverConfigOption} */ (context.options?.[optionIndex])
42
+ ) ??
43
+ get(/** @type {ResolverConfigOption} */ (context.settings?.n)) ??
44
+ get(/** @type {ResolverConfigOption} */ (context.settings?.node)) ??
39
45
  DEFAULT_VALUE
40
46
  )
41
47
  }
@@ -24,11 +24,16 @@ const DEFAULT_TS_VALUE = [
24
24
  /**
25
25
  * Gets `tryExtensions` property from a given option object.
26
26
  *
27
- * @param {{ tryExtensions: unknown[] } | undefined} option - An option object to get.
27
+ * @param {unknown} option - An option object to get.
28
28
  * @returns {string[] | undefined} The `tryExtensions` value, or `null`.
29
29
  */
30
30
  function get(option) {
31
- if (Array.isArray(option?.tryExtensions)) {
31
+ if (
32
+ option != null &&
33
+ typeof option === "object" &&
34
+ "tryExtensions" in option &&
35
+ Array.isArray(option?.tryExtensions)
36
+ ) {
32
37
  return option.tryExtensions.map(String)
33
38
  }
34
39
  }
@@ -1,6 +1,7 @@
1
1
  "use strict"
2
2
 
3
3
  const { getTsconfig, parseTsconfig } = require("get-tsconfig")
4
+ const { getPhysicalFilename, getFilename } = require("./eslint-compat")
4
5
  const fsCache = new Map()
5
6
 
6
7
  /**
@@ -30,12 +31,7 @@ function getTSConfigForFile(filename) {
30
31
  * @returns {import("get-tsconfig").TsConfigResult | null}
31
32
  */
32
33
  function getTSConfigForContext(context) {
33
- // TODO: remove context.get(PhysicalFilename|Filename) when dropping eslint < v10
34
- const filename =
35
- context.physicalFilename ??
36
- context.getPhysicalFilename?.() ??
37
- context.filename ??
38
- context.getFilename?.()
34
+ const filename = getPhysicalFilename(context) ?? getFilename(context)
39
35
 
40
36
  return getTSConfigForFile(filename)
41
37
  }
@@ -129,8 +129,12 @@ function getFromTSConfigFromFile(context) {
129
129
  */
130
130
  module.exports = function getTypescriptExtensionMap(context) {
131
131
  return (
132
- get(context.options?.[0]) ||
133
- get(context.settings?.n ?? context.settings?.node) ||
132
+ get(/** @type {Options} */ (context.options?.[0])) ||
133
+ get(
134
+ /** @type {Options} */ (
135
+ context.settings?.n ?? context.settings?.node
136
+ )
137
+ ) ||
134
138
  getFromTSConfigFromFile(context) ||
135
139
  PRESERVE_MAPPING
136
140
  )
@@ -176,6 +176,20 @@ module.exports = class ImportTarget {
176
176
  return "node"
177
177
  }
178
178
 
179
+ // This check should be done before the RegExp that checks npm packages,
180
+ // because `tsconfig.json` aliases may start with `~`, `@` and this will
181
+ // cause imports using aliases to be treated as npm-module imports
182
+ // https://github.com/eslint-community/eslint-plugin-n/issues/379
183
+ if (isTypescript(this.context)) {
184
+ const aliases = getTSConfigAliases(this.context)
185
+ if (
186
+ Array.isArray(aliases) &&
187
+ aliases.some(alias => this.name.startsWith(alias.name))
188
+ ) {
189
+ return "relative"
190
+ }
191
+ }
192
+
179
193
  if (/^(@[\w~-][\w.~-]*\/)?[\w~-][\w.~-]*/.test(this.name)) {
180
194
  return "npm"
181
195
  }
@@ -331,7 +345,11 @@ module.exports = class ImportTarget {
331
345
 
332
346
  const requireResolve = resolver.create.sync(this.resolverConfig)
333
347
 
334
- const cwd = this.context.settings?.cwd ?? process.cwd()
348
+ const cwd =
349
+ typeof this.context.settings?.cwd === "string"
350
+ ? this.context.settings?.cwd
351
+ : process.cwd()
352
+
335
353
  for (const directory of this.getPaths()) {
336
354
  const baseDir = resolve(cwd, directory)
337
355
 
@@ -1,6 +1,7 @@
1
1
  "use strict"
2
2
 
3
3
  const path = require("path")
4
+ const { getPhysicalFilename, getFilename } = require("./eslint-compat")
4
5
 
5
6
  const typescriptExtensions = [".ts", ".tsx", ".cts", ".mts"]
6
7
 
@@ -12,10 +13,7 @@ const typescriptExtensions = [".ts", ".tsx", ".cts", ".mts"]
12
13
  */
13
14
  module.exports = function isTypescript(context) {
14
15
  const sourceFileExt = path.extname(
15
- context.physicalFilename ??
16
- context.getPhysicalFilename?.() ??
17
- context.filename ??
18
- context.getFilename?.()
16
+ getPhysicalFilename(context) ?? getFilename(context)
19
17
  )
20
18
  return typescriptExtensions.includes(sourceFileExt)
21
19
  }
@@ -11,7 +11,7 @@ const getResolverConfig = require("./get-resolver-config")
11
11
  const getTryExtensions = require("./get-try-extensions")
12
12
  const ImportTarget = require("./import-target")
13
13
  const stripImportPathParams = require("./strip-import-path-params")
14
-
14
+ const { getFilename } = require("./eslint-compat")
15
15
  /** @typedef {import('@typescript-eslint/typescript-estree').TSESTree.ImportDeclaration} ImportDeclaration */
16
16
 
17
17
  /**
@@ -38,9 +38,7 @@ module.exports = function visitImport(
38
38
  ) {
39
39
  /** @type {import('./import-target.js')[]} */
40
40
  const targets = []
41
- const basedir = path.dirname(
42
- path.resolve(context.filename ?? context.getFilename())
43
- )
41
+ const basedir = path.dirname(path.resolve(getFilename(context)))
44
42
  const paths = getResolvePaths(context, optionIndex)
45
43
  const resolverConfig = getResolverConfig(context, optionIndex)
46
44
  const extensions = getTryExtensions(context, optionIndex)
@@ -16,6 +16,7 @@ const getResolverConfig = require("./get-resolver-config")
16
16
  const getTryExtensions = require("./get-try-extensions")
17
17
  const ImportTarget = require("./import-target")
18
18
  const stripImportPathParams = require("./strip-import-path-params")
19
+ const { getScope, getFilename } = require("../util/eslint-compat")
19
20
 
20
21
  /**
21
22
  * @typedef VisitRequireOptions
@@ -39,20 +40,15 @@ module.exports = function visitRequire(
39
40
  ) {
40
41
  /** @type {import('./import-target.js')[]} */
41
42
  const targets = []
42
- const basedir = path.dirname(
43
- path.resolve(context.filename ?? context.getFilename())
44
- )
43
+ const basedir = path.dirname(path.resolve(getFilename(context)))
45
44
  const paths = getResolvePaths(context)
46
45
  const resolverConfig = getResolverConfig(context)
47
46
  const extensions = getTryExtensions(context)
48
47
  const options = { basedir, paths, extensions, resolverConfig }
49
48
 
50
49
  return {
51
- "Program:exit"(node) {
52
- const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
53
- const tracker = new ReferenceTracker(
54
- sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9
55
- )
50
+ "Program:exit"() {
51
+ const tracker = new ReferenceTracker(getScope(context))
56
52
  const references = tracker.iterateGlobalReferences({
57
53
  require: {
58
54
  [CALL]: true,
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "eslint-plugin-n",
3
- "version": "17.15.1",
3
+ "version": "17.16.2",
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"
7
7
  },
8
8
  "main": "lib/index.js",
9
+ "type": "commonjs",
9
10
  "types": "types/index.d.ts",
10
11
  "files": [
11
12
  "lib/",