eslint-plugin-n 17.17.0 → 17.19.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
@@ -156,6 +156,7 @@ For [Shareable Configs](https://eslint.org/docs/latest/developer-guide/shareable
156
156
  | [no-restricted-import](docs/rules/no-restricted-import.md) | disallow specified modules when loaded by `import` declarations | | | |
157
157
  | [no-restricted-require](docs/rules/no-restricted-require.md) | disallow specified modules when loaded by `require` | | | |
158
158
  | [no-sync](docs/rules/no-sync.md) | disallow synchronous methods | | | |
159
+ | [no-top-level-await](docs/rules/no-top-level-await.md) | disallow top-level `await` in published modules | | | |
159
160
  | [no-unpublished-bin](docs/rules/no-unpublished-bin.md) | disallow `bin` files that npm ignores | 🟢 ✅ | | |
160
161
  | [no-unpublished-import](docs/rules/no-unpublished-import.md) | disallow `import` declarations which import private modules | 🟢 ✅ | | |
161
162
  | [no-unpublished-require](docs/rules/no-unpublished-require.md) | disallow `require()` expressions which import private modules | 🟢 ✅ | | |
package/lib/index.js CHANGED
@@ -34,6 +34,7 @@ const base = {
34
34
  "no-restricted-import": require("./rules/no-restricted-import"),
35
35
  "no-restricted-require": require("./rules/no-restricted-require"),
36
36
  "no-sync": require("./rules/no-sync"),
37
+ "no-top-level-await": require("./rules/no-top-level-await"),
37
38
  "no-unpublished-bin": require("./rules/no-unpublished-bin"),
38
39
  "no-unpublished-import": require("./rules/no-unpublished-import"),
39
40
  "no-unpublished-require": require("./rules/no-unpublished-require"),
@@ -16,6 +16,9 @@ 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
18
  const { getScope } = require("../util/eslint-compat")
19
+ const {
20
+ iterateProcessGetBuiltinModuleReferences,
21
+ } = require("../util/iterate-process-get-builtin-module-references")
19
22
 
20
23
  /** @typedef {import('../unsupported-features/types.js').DeprecatedInfo} DeprecatedInfo */
21
24
  /**
@@ -842,6 +845,10 @@ module.exports = {
842
845
  }
843
846
  for (const report of [
844
847
  ...tracker.iterateCjsReferences(modules),
848
+ ...iterateProcessGetBuiltinModuleReferences(
849
+ tracker,
850
+ modules
851
+ ),
845
852
  ...tracker.iterateEsmReferences(modules),
846
853
  ]) {
847
854
  const { node, path, type, info } = report
@@ -4,6 +4,22 @@
4
4
  */
5
5
  "use strict"
6
6
 
7
+ let typeMatchesSpecifier =
8
+ /** @type {import('ts-declaration-location').default | undefined} */
9
+ (undefined)
10
+
11
+ try {
12
+ typeMatchesSpecifier =
13
+ /** @type {import('ts-declaration-location').default} */ (
14
+ /** @type {unknown} */ (require("ts-declaration-location"))
15
+ )
16
+
17
+ // eslint-disable-next-line no-empty -- Deliberately left empty.
18
+ } catch {}
19
+ const getTypeOfNode = require("../util/get-type-of-node")
20
+ const getParserServices = require("../util/get-parser-services")
21
+ const getFullTypeName = require("../util/get-full-type-name")
22
+
7
23
  const selectors = [
8
24
  // fs.readFileSync()
9
25
  // readFileSync.call(null, 'path')
@@ -16,7 +32,7 @@ const selectors = [
16
32
  * @typedef {[
17
33
  * {
18
34
  * allowAtRootLevel?: boolean
19
- * ignores?: string[]
35
+ * ignores?: (string | { from: "file"; path?: string; name?: string[]; } | { from: "package"; package?: string; name?: string[]; } | { from: "lib"; name?: string[]; })[]
20
36
  * }?
21
37
  * ]} RuleOptions
22
38
  */
@@ -40,7 +56,56 @@ module.exports = {
40
56
  },
41
57
  ignores: {
42
58
  type: "array",
43
- items: { type: "string" },
59
+ items: {
60
+ oneOf: [
61
+ { type: "string" },
62
+ {
63
+ type: "object",
64
+ properties: {
65
+ from: { const: "file" },
66
+ path: {
67
+ type: "string",
68
+ },
69
+ name: {
70
+ type: "array",
71
+ items: {
72
+ type: "string",
73
+ },
74
+ },
75
+ },
76
+ additionalProperties: false,
77
+ },
78
+ {
79
+ type: "object",
80
+ properties: {
81
+ from: { const: "lib" },
82
+ name: {
83
+ type: "array",
84
+ items: {
85
+ type: "string",
86
+ },
87
+ },
88
+ },
89
+ additionalProperties: false,
90
+ },
91
+ {
92
+ type: "object",
93
+ properties: {
94
+ from: { const: "package" },
95
+ package: {
96
+ type: "string",
97
+ },
98
+ name: {
99
+ type: "array",
100
+ items: {
101
+ type: "string",
102
+ },
103
+ },
104
+ },
105
+ additionalProperties: false,
106
+ },
107
+ ],
108
+ },
44
109
  default: [],
45
110
  },
46
111
  },
@@ -65,15 +130,70 @@ module.exports = {
65
130
  * @returns {void}
66
131
  */
67
132
  [selector.join(",")](node) {
68
- if (ignores.includes(node.name)) {
69
- return
133
+ const parserServices = getParserServices(context)
134
+
135
+ /**
136
+ * @type {import('typescript').Type | undefined | null}
137
+ */
138
+ let type = undefined
139
+
140
+ /**
141
+ * @type {string | undefined | null}
142
+ */
143
+ let fullName = undefined
144
+
145
+ for (const ignore of ignores) {
146
+ if (typeof ignore === "string") {
147
+ if (ignore === node.name) {
148
+ return
149
+ }
150
+
151
+ continue
152
+ }
153
+
154
+ if (
155
+ parserServices === null ||
156
+ parserServices.program === null
157
+ ) {
158
+ throw new Error(
159
+ 'TypeScript parser services not available. Rule "n/no-sync" is configured to use "ignores" option with a non-string value. This requires TypeScript parser services to be available.'
160
+ )
161
+ }
162
+
163
+ if (typeMatchesSpecifier === undefined) {
164
+ throw new Error(
165
+ 'ts-declaration-location not available. Rule "n/no-sync" is configured to use "ignores" option with a non-string value. This requires ts-declaration-location to be available.'
166
+ )
167
+ }
168
+
169
+ type =
170
+ type === undefined
171
+ ? getTypeOfNode(node, parserServices)
172
+ : type
173
+
174
+ fullName =
175
+ fullName === undefined
176
+ ? getFullTypeName(type)
177
+ : fullName
178
+
179
+ if (
180
+ typeMatchesSpecifier(
181
+ parserServices.program,
182
+ ignore,
183
+ type
184
+ ) &&
185
+ (ignore.name === undefined ||
186
+ ignore.name.includes(fullName ?? node.name))
187
+ ) {
188
+ return
189
+ }
70
190
  }
71
191
 
72
192
  context.report({
73
193
  node: node.parent,
74
194
  messageId: "noSync",
75
195
  data: {
76
- propertyName: node.name,
196
+ propertyName: fullName ?? node.name,
77
197
  },
78
198
  })
79
199
  },
@@ -0,0 +1,132 @@
1
+ /**
2
+ * @author Yosuke Ota <https://github.com/ota-meshi>
3
+ * See LICENSE file in root directory for full license.
4
+ */
5
+ "use strict"
6
+
7
+ const path = require("path")
8
+ const { getSourceCode } = require("../util/eslint-compat")
9
+ const getConvertPath = require("../util/get-convert-path")
10
+ const { getPackageJson } = require("../util/get-package-json")
11
+ const { isBinFile } = require("../util/is-bin-file")
12
+ const getNpmignore = require("../util/get-npmignore")
13
+
14
+ const HASHBANG_ENV = "#!/usr/bin/env"
15
+
16
+ /**
17
+ * @typedef {[
18
+ * {
19
+ * ignoreBin?:boolean
20
+ * convertPath?: import('../util/get-convert-path').ConvertPath;
21
+ * }?
22
+ * ]} RuleOptions
23
+ */
24
+
25
+ /**
26
+ * Checks whether the code has a hashbang comment or not.
27
+ * @param {import('./rule-module').PluginRuleContext<{RuleOptions: RuleOptions}>} context
28
+ */
29
+ function hasHashbang(context) {
30
+ const sourceCode = getSourceCode(context)
31
+ return Boolean(sourceCode.text.startsWith(HASHBANG_ENV))
32
+ }
33
+
34
+ /** @param {import('./rule-module').PluginRuleContext<{RuleOptions: RuleOptions}>} context */
35
+ function ignore(context) {
36
+ const options = context.options[0] || {}
37
+ const ignoreBin = options.ignoreBin ?? false
38
+ if (ignoreBin && hasHashbang(context)) {
39
+ // If the code has a hashbang comment, it is considered an executable file.
40
+ return true
41
+ }
42
+
43
+ const filePath = context.filename ?? context.getFilename()
44
+ if (filePath === "<input>") {
45
+ // The file path is "<input>" (not specified), so it will be ignored.
46
+ return true
47
+ }
48
+ const originalAbsolutePath = path.resolve(filePath)
49
+
50
+ // Find package.json
51
+ const packageJson = getPackageJson(originalAbsolutePath)
52
+ if (typeof packageJson?.filePath !== "string") {
53
+ // The file is not in a package, so it will be ignored.
54
+ return true
55
+ }
56
+
57
+ // Convert by convertPath option
58
+ const packageDirectory = path.dirname(packageJson.filePath)
59
+ const convertedRelativePath = getConvertPath(context)(
60
+ path
61
+ .relative(packageDirectory, originalAbsolutePath)
62
+ .replace(/\\/gu, "/")
63
+ )
64
+ const convertedAbsolutePath = path.resolve(
65
+ packageDirectory,
66
+ convertedRelativePath
67
+ )
68
+
69
+ if (
70
+ ignoreBin &&
71
+ isBinFile(convertedAbsolutePath, packageJson.bin, packageDirectory)
72
+ ) {
73
+ // The file is defined in the `bin` field of `package.json`
74
+ return true
75
+ }
76
+
77
+ // Check ignored or not
78
+ const npmignore = getNpmignore(convertedAbsolutePath)
79
+ if (npmignore.match(convertedRelativePath)) {
80
+ // The file is unpublished file, so it will be ignored.
81
+ return true
82
+ }
83
+
84
+ return false
85
+ }
86
+
87
+ /** @type {import('./rule-module').RuleModule<{RuleOptions: RuleOptions}>} */
88
+ module.exports = {
89
+ meta: {
90
+ docs: {
91
+ description: "disallow top-level `await` in published modules",
92
+ recommended: false,
93
+ url: "https://github.com/eslint-community/eslint-plugin-n/blob/HEAD/docs/rules/no-top-level-await.md",
94
+ },
95
+ fixable: null,
96
+ messages: {
97
+ forbidden: "Top-level `await` is forbidden in published modules.",
98
+ },
99
+ schema: [
100
+ {
101
+ type: "object",
102
+ properties: {
103
+ ignoreBin: { type: "boolean" },
104
+ convertPath: getConvertPath.schema,
105
+ },
106
+ additionalProperties: false,
107
+ },
108
+ ],
109
+ type: "problem",
110
+ },
111
+ create(context) {
112
+ if (ignore(context)) {
113
+ return {}
114
+ }
115
+ let functionDepth = 0
116
+ return {
117
+ ":function"() {
118
+ functionDepth++
119
+ },
120
+ ":function:exit"() {
121
+ functionDepth--
122
+ },
123
+ "AwaitExpression, ForOfStatement[await=true]"(node) {
124
+ if (functionDepth > 0) {
125
+ // not top-level
126
+ return
127
+ }
128
+ context.report({ node, messageId: "forbidden" })
129
+ },
130
+ }
131
+ },
132
+ }
@@ -9,6 +9,7 @@ const getAllowModules = require("../util/get-allow-modules")
9
9
  const getConvertPath = require("../util/get-convert-path")
10
10
  const getResolvePaths = require("../util/get-resolve-paths")
11
11
  const getResolverConfig = require("../util/get-resolver-config")
12
+ const getTryExtensions = require("../util/get-try-extensions")
12
13
  const visitImport = require("../util/visit-import")
13
14
 
14
15
  /**
@@ -18,6 +19,7 @@ const visitImport = require("../util/visit-import")
18
19
  * convertPath?: import('../util/get-convert-path').ConvertPath;
19
20
  * resolvePaths?: import('../util/get-resolve-paths').ResolvePaths;
20
21
  * resolverConfig?: import('../util/get-resolver-config').ResolverConfig;
22
+ * tryExtensions?: import('../util/get-try-extensions').TryExtensions;
21
23
  * ignoreTypeImport?: boolean;
22
24
  * ignorePrivate?: boolean;
23
25
  * }?
@@ -42,6 +44,7 @@ module.exports = {
42
44
  convertPath: getConvertPath.schema,
43
45
  resolvePaths: getResolvePaths.schema,
44
46
  resolverConfig: getResolverConfig.schema,
47
+ tryExtensions: getTryExtensions.schema,
45
48
  ignoreTypeImport: { type: "boolean", default: false },
46
49
  ignorePrivate: { type: "boolean", default: true },
47
50
  },
@@ -4,7 +4,10 @@
4
4
  */
5
5
  "use strict"
6
6
 
7
- const { getStringIfConstant } = require("@eslint-community/eslint-utils")
7
+ const {
8
+ getStringIfConstant,
9
+ getPropertyName,
10
+ } = require("@eslint-community/eslint-utils")
8
11
 
9
12
  const { Range } = require("semver")
10
13
 
@@ -15,6 +18,10 @@ const {
15
18
  NodeBuiltinModules,
16
19
  } = require("../unsupported-features/node-builtins.js")
17
20
 
21
+ /**
22
+ * @typedef { 'import' | 'require' | 'getBuiltinModule' } ModuleStyle
23
+ */
24
+
18
25
  /**
19
26
  * @param {string} name The name of the node module
20
27
  * @returns {boolean}
@@ -38,10 +45,15 @@ function isStringLiteral(node) {
38
45
 
39
46
  /**
40
47
  * @param {import('eslint').Rule.RuleContext} context
41
- * @param {import('../util/import-target.js').ModuleStyle} moduleStyle
48
+ * @param {ModuleStyle} moduleStyle
42
49
  * @returns {boolean}
43
50
  */
44
51
  function isEnablingThisRule(context, moduleStyle) {
52
+ // The availability of `process.getBuiltinModule()` means that `node:` protocol is supported.
53
+ if (moduleStyle === "getBuiltinModule") {
54
+ return true
55
+ }
56
+
45
57
  const version = getConfiguredNodeVersion(context)
46
58
 
47
59
  // Only check Node.js version because this rule is meaningless if configured Node.js version doesn't match semver range.
@@ -81,7 +93,7 @@ function isValidRequireArgument(node) {
81
93
  /**
82
94
  * @param {import('estree').Node | null | undefined} node
83
95
  * @param {import('eslint').Rule.RuleContext} context
84
- * @param {import('../util/import-target.js').ModuleStyle} moduleStyle
96
+ * @param {ModuleStyle} moduleStyle
85
97
  */
86
98
  function validate(node, context, moduleStyle) {
87
99
  if (node == null) {
@@ -96,7 +108,10 @@ function validate(node, context, moduleStyle) {
96
108
  return
97
109
  }
98
110
 
99
- if (moduleStyle === "require" && !isValidRequireArgument(node)) {
111
+ if (
112
+ (moduleStyle === "require" || moduleStyle === "getBuiltinModule") &&
113
+ !isValidRequireArgument(node)
114
+ ) {
100
115
  return
101
116
  }
102
117
 
@@ -126,6 +141,25 @@ function validate(node, context, moduleStyle) {
126
141
  })
127
142
  }
128
143
 
144
+ /**
145
+ * @param {import('estree').Expression | import('estree').Super} node
146
+ */
147
+ function isProcess(node) {
148
+ if (node.type === "Identifier" && node.name === "process") {
149
+ return true
150
+ }
151
+ if (node.type === "MemberExpression") {
152
+ if (getPropertyName(node) !== "process") {
153
+ return false
154
+ }
155
+ return (
156
+ node.object.type === "Identifier" &&
157
+ node.object.name === "globalThis"
158
+ )
159
+ }
160
+ return false
161
+ }
162
+
129
163
  /** @type {import('./rule-module').RuleModule} */
130
164
  module.exports = {
131
165
  meta: {
@@ -158,15 +192,25 @@ module.exports = {
158
192
  }
159
193
 
160
194
  if (
161
- node.optional ||
162
- node.arguments.length !== 1 ||
163
- node.callee.type !== "Identifier" ||
164
- node.callee.name !== "require"
195
+ !node.optional &&
196
+ node.arguments.length === 1 &&
197
+ node.callee.type === "Identifier" &&
198
+ node.callee.name === "require"
165
199
  ) {
166
- return
200
+ return validate(node.arguments[0], context, "require")
201
+ }
202
+ if (
203
+ node.arguments.length >= 1 &&
204
+ node.callee.type === "MemberExpression" &&
205
+ isProcess(node.callee.object) &&
206
+ getPropertyName(node.callee) === "getBuiltinModule"
207
+ ) {
208
+ return validate(
209
+ node.arguments[0],
210
+ context,
211
+ "getBuiltinModule"
212
+ )
167
213
  }
168
-
169
- return validate(node.arguments[0], context, "require")
170
214
  },
171
215
 
172
216
  ExportAllDeclaration(node) {
@@ -10,6 +10,9 @@ const {
10
10
  ReferenceTracker,
11
11
  } = require("@eslint-community/eslint-utils")
12
12
  const { getScope } = require("../../util/eslint-compat")
13
+ const {
14
+ iterateProcessGetBuiltinModuleReferences,
15
+ } = require("../../util/iterate-process-get-builtin-module-references")
13
16
 
14
17
  /** @type {import('@eslint-community/eslint-utils').TraceMap<boolean>} */
15
18
  const dns = {
@@ -63,6 +66,10 @@ module.exports = {
63
66
  const tracker = new ReferenceTracker(scope, { mode: "legacy" })
64
67
  const references = [
65
68
  ...tracker.iterateCjsReferences(traceMap),
69
+ ...iterateProcessGetBuiltinModuleReferences(
70
+ tracker,
71
+ traceMap
72
+ ),
66
73
  ...tracker.iterateEsmReferences(traceMap),
67
74
  ]
68
75
 
@@ -6,6 +6,9 @@
6
6
 
7
7
  const { CALL, ReferenceTracker } = require("@eslint-community/eslint-utils")
8
8
  const { getScope } = require("../../util/eslint-compat")
9
+ const {
10
+ iterateProcessGetBuiltinModuleReferences,
11
+ } = require("../../util/iterate-process-get-builtin-module-references")
9
12
 
10
13
  /** @type {import('@eslint-community/eslint-utils').TraceMap<boolean>} */
11
14
  const traceMap = {
@@ -61,6 +64,10 @@ module.exports = {
61
64
  const tracker = new ReferenceTracker(scope, { mode: "legacy" })
62
65
  const references = [
63
66
  ...tracker.iterateCjsReferences(traceMap),
67
+ ...iterateProcessGetBuiltinModuleReferences(
68
+ tracker,
69
+ traceMap
70
+ ),
64
71
  ...tracker.iterateEsmReferences(traceMap),
65
72
  ]
66
73
 
@@ -5,7 +5,7 @@ const { READ } = require("@eslint-community/eslint-utils")
5
5
  /**
6
6
  * @satisfies {import('../types.js').SupportVersionTraceMap}
7
7
  */
8
- const http = {
8
+ const https = {
9
9
  globalAgent: { [READ]: { supported: ["0.5.9"] } },
10
10
  createServer: { [READ]: { supported: ["0.3.4"] } },
11
11
  get: { [READ]: { supported: ["0.3.6"] } },
@@ -18,12 +18,12 @@ const http = {
18
18
  * @satisfies {import('../types.js').SupportVersionTraceMap}
19
19
  */
20
20
  module.exports = {
21
- http: {
21
+ https: {
22
22
  [READ]: { supported: ["0.3.4"] },
23
- ...http,
23
+ ...https,
24
24
  },
25
- "node:http": {
25
+ "node:https": {
26
26
  [READ]: { supported: ["14.13.1", "12.20.0"] },
27
- ...http,
27
+ ...https,
28
28
  },
29
29
  }
@@ -37,6 +37,7 @@ const NodeBuiltinModules = {
37
37
  ...require("./node-builtins-modules/sea.js"),
38
38
  ...require("./node-builtins-modules/stream.js"),
39
39
  ...require("./node-builtins-modules/string_decoder.js"),
40
+ ...require("./node-builtins-modules/sqlite.js"),
40
41
  ...require("./node-builtins-modules/test.js"),
41
42
  ...require("./node-builtins-modules/timers.js"),
42
43
  ...require("./node-builtins-modules/tls.js"),
@@ -6,6 +6,9 @@
6
6
 
7
7
  const { ReferenceTracker } = require("@eslint-community/eslint-utils")
8
8
  const { getScope } = require("../util/eslint-compat")
9
+ const {
10
+ iterateProcessGetBuiltinModuleReferences,
11
+ } = require("../util/iterate-process-get-builtin-module-references")
9
12
  /**
10
13
  * @typedef TraceMap
11
14
  * @property {import('@eslint-community/eslint-utils').TraceMap<boolean>} globals
@@ -43,6 +46,10 @@ class Verifier {
43
46
 
44
47
  for (const { node } of [
45
48
  ...tracker.iterateCjsReferences(traceMap.modules),
49
+ ...iterateProcessGetBuiltinModuleReferences(
50
+ tracker,
51
+ traceMap.modules
52
+ ),
46
53
  ...tracker.iterateEsmReferences(traceMap.modules),
47
54
  ]) {
48
55
  context.report({ node, messageId: "preferGlobal" })
@@ -11,6 +11,9 @@ const getSemverRange = require("./get-semver-range")
11
11
  const unprefixNodeColon = require("./unprefix-node-colon")
12
12
  const semverRangeSubset = require("semver/ranges/subset")
13
13
  const { getScope } = require("../util/eslint-compat")
14
+ const {
15
+ iterateProcessGetBuiltinModuleReferences,
16
+ } = require("./iterate-process-get-builtin-module-references")
14
17
 
15
18
  /**
16
19
  * Parses the options.
@@ -158,6 +161,10 @@ module.exports.checkUnsupportedBuiltins = function checkUnsupportedBuiltins(
158
161
  const tracker = new ReferenceTracker(scope, { mode: "legacy" })
159
162
  const references = [
160
163
  ...tracker.iterateCjsReferences(traceMap.modules ?? {}),
164
+ ...iterateProcessGetBuiltinModuleReferences(
165
+ tracker,
166
+ traceMap.modules ?? {}
167
+ ),
161
168
  ...tracker.iterateEsmReferences(traceMap.modules ?? {}),
162
169
  ...tracker.iterateGlobalReferences(traceMap.globals ?? {}),
163
170
  ]
@@ -0,0 +1,47 @@
1
+ "use strict"
2
+
3
+ const ts = (() => {
4
+ try {
5
+ // eslint-disable-next-line n/no-unpublished-require
6
+ return require("typescript")
7
+ } catch {
8
+ return null
9
+ }
10
+ })()
11
+
12
+ /**
13
+ * @param {import('typescript').Type | null} type
14
+ * @returns {string | null}
15
+ */
16
+ module.exports = function getFullTypeName(type) {
17
+ if (ts === null || type === null) {
18
+ return null
19
+ }
20
+
21
+ /**
22
+ * @type {string[]}
23
+ */
24
+ let nameParts = []
25
+ let currentSymbol = type.getSymbol()
26
+ while (currentSymbol !== undefined) {
27
+ if (
28
+ currentSymbol.valueDeclaration?.kind === ts.SyntaxKind.SourceFile ||
29
+ currentSymbol.valueDeclaration?.kind ===
30
+ ts.SyntaxKind.ModuleDeclaration
31
+ ) {
32
+ break
33
+ }
34
+
35
+ nameParts.unshift(currentSymbol.getName())
36
+ currentSymbol =
37
+ /** @type {import('typescript').Symbol & {parent: import('typescript').Symbol | undefined}} */ (
38
+ currentSymbol
39
+ ).parent
40
+ }
41
+
42
+ if (nameParts.length === 0) {
43
+ return null
44
+ }
45
+
46
+ return nameParts.join(".")
47
+ }
@@ -0,0 +1,24 @@
1
+ "use strict"
2
+
3
+ const {
4
+ getParserServices: getParserServicesFromTsEslint,
5
+ } = require("@typescript-eslint/utils/eslint-utils")
6
+
7
+ /**
8
+ * Get the TypeScript parser services.
9
+ * If TypeScript isn't present, returns `null`.
10
+ *
11
+ * @param {import('eslint').Rule.RuleContext} context - rule context
12
+ * @returns {import('@typescript-eslint/parser').ParserServices | null}
13
+ */
14
+ module.exports = function getParserServices(context) {
15
+ // Not using tseslint parser?
16
+ if (
17
+ context.sourceCode.parserServices?.esTreeNodeToTSNodeMap == null ||
18
+ context.sourceCode.parserServices.tsNodeToESTreeNodeMap == null
19
+ ) {
20
+ return null
21
+ }
22
+
23
+ return getParserServicesFromTsEslint(/** @type {any} */ (context), true)
24
+ }
@@ -0,0 +1,21 @@
1
+ "use strict"
2
+
3
+ /**
4
+ * Get the type of a node.
5
+ * If TypeScript isn't present, returns `null`.
6
+ *
7
+ * @param {import('estree').Node} node - A node
8
+ * @param {import('@typescript-eslint/parser').ParserServices} parserServices - A parserServices
9
+ * @returns {import('typescript').Type | null}
10
+ */
11
+ module.exports = function getTypeOfNode(node, parserServices) {
12
+ const { esTreeNodeToTSNodeMap, program } = parserServices
13
+ if (program === null) {
14
+ return null
15
+ }
16
+ const tsNode = esTreeNodeToTSNodeMap.get(/** @type {any} */ (node))
17
+ const checker = program.getTypeChecker()
18
+ const nodeType = checker.getTypeAtLocation(tsNode)
19
+ const constrained = checker.getBaseConstraintOfType(nodeType)
20
+ return constrained ?? nodeType
21
+ }
@@ -0,0 +1,56 @@
1
+ "use strict"
2
+ const {
3
+ CALL,
4
+ getStringIfConstant,
5
+ READ,
6
+ } = require("@eslint-community/eslint-utils")
7
+ const processGetBuiltinModuleCall = {
8
+ process: {
9
+ getBuiltinModule: {
10
+ [CALL]: true,
11
+ },
12
+ },
13
+ }
14
+ /**
15
+ * Iterate the references of process.getBuiltinModule() modules.
16
+ * @template Info
17
+ * @param {import("@eslint-community/eslint-utils").ReferenceTracker} tracker The reference tracker.
18
+ * @param {import("@eslint-community/eslint-utils").TraceMap<Info>} traceMap The trace map.
19
+ * @returns {IterableIterator<import("@eslint-community/eslint-utils").Reference<Info>>} The iterator.
20
+ */
21
+ function* iterateProcessGetBuiltinModuleReferences(tracker, traceMap) {
22
+ for (const { node } of tracker.iterateGlobalReferences(
23
+ processGetBuiltinModuleCall
24
+ )) {
25
+ if (node.type !== "CallExpression") continue
26
+ const key = node.arguments[0] && getStringIfConstant(node.arguments[0])
27
+ if (key == null) {
28
+ continue
29
+ }
30
+ const nextTraceMap = Object.hasOwn(traceMap, key) && traceMap[key]
31
+ if (!nextTraceMap) {
32
+ continue
33
+ }
34
+
35
+ if (nextTraceMap[READ]) {
36
+ yield {
37
+ node,
38
+ path: [key],
39
+ type: READ,
40
+ info: nextTraceMap[READ],
41
+ }
42
+ }
43
+
44
+ for (const ref of tracker.iteratePropertyReferences(
45
+ node,
46
+ nextTraceMap
47
+ )) {
48
+ yield {
49
+ ...ref,
50
+ path: [key, ...ref.path],
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ module.exports = { iterateProcessGetBuiltinModuleReferences }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-n",
3
- "version": "17.17.0",
3
+ "version": "17.19.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"
@@ -18,21 +18,23 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@eslint-community/eslint-utils": "^4.5.0",
21
+ "@typescript-eslint/utils": "^8.26.1",
21
22
  "enhanced-resolve": "^5.17.1",
22
23
  "eslint-plugin-es-x": "^7.8.0",
23
24
  "get-tsconfig": "^4.8.1",
24
25
  "globals": "^15.11.0",
25
26
  "ignore": "^5.3.2",
26
27
  "minimatch": "^9.0.5",
27
- "semver": "^7.6.3"
28
+ "semver": "^7.6.3",
29
+ "ts-declaration-location": "^1.0.6"
28
30
  },
29
31
  "devDependencies": {
30
32
  "@eslint/js": "^9.14.0",
31
33
  "@types/eslint": "^9.6.1",
32
34
  "@types/estree": "^1.0.6",
33
35
  "@types/node": "^20.17.5",
34
- "@typescript-eslint/parser": "^8.12.2",
35
- "@typescript-eslint/typescript-estree": "^8.12.2",
36
+ "@typescript-eslint/parser": "^8.26.1",
37
+ "@typescript-eslint/typescript-estree": "^8.26.1",
36
38
  "eslint": "^9.14.0",
37
39
  "eslint-config-prettier": "^9.1.0",
38
40
  "eslint-doc-generator": "^1.7.1",
@@ -120,4 +122,4 @@
120
122
  "imports": {
121
123
  "#test-helpers": "./tests/test-helpers.js"
122
124
  }
123
- }
125
+ }