@@ -8,13 +8,12 @@ import { getPhysicalFilename } from 'eslint-module-utils/contextCompat';
8
8
import { getFileExtensions } from 'eslint-module-utils/ignore' ;
9
9
import resolve from 'eslint-module-utils/resolve' ;
10
10
import visit from 'eslint-module-utils/visit' ;
11
- import { dirname , join , resolve as resolvePath } from 'path' ;
11
+ import { dirname , join } from 'path' ;
12
12
import readPkgUp from 'eslint-module-utils/readPkgUp' ;
13
13
import values from 'object.values' ;
14
14
import includes from 'array-includes' ;
15
15
import flatMap from 'array.prototype.flatmap' ;
16
16
17
- import { walkSync } from '../core/fsWalk' ;
18
17
import ExportMapBuilder from '../exportMap/builder' ;
19
18
import recursivePatternCapture from '../exportMap/patternCapture' ;
20
19
import docsUrl from '../docsUrl' ;
@@ -51,21 +50,62 @@ function requireFileEnumerator() {
51
50
}
52
51
53
52
/**
54
- *
53
+ * Given a FileEnumerator class, instantiate and load the list of files.
55
54
* @param FileEnumerator the `FileEnumerator` class from `eslint`'s internal api
56
55
* @param {string } src path to the src root
57
56
* @param {string[] } extensions list of supported extensions
58
57
* @returns {{ filename: string, ignored: boolean }[] } list of files to operate on
59
58
*/
60
59
function listFilesUsingFileEnumerator ( FileEnumerator , src , extensions ) {
61
- const e = new FileEnumerator ( {
60
+ // We need to know whether this is being run with flat config in order to
61
+ // determine how to report errors if FileEnumerator throws due to a lack of eslintrc.
62
+
63
+ const { ESLINT_USE_FLAT_CONFIG } = process . env ;
64
+
65
+ // This condition is sufficient to test in v8, since the environment variable is necessary to turn on flat config
66
+ let isUsingFlatConfig = ESLINT_USE_FLAT_CONFIG && process . env . ESLINT_USE_FLAT_CONFIG !== 'false' ;
67
+
68
+ // In the case of using v9, we can check the `shouldUseFlatConfig` function
69
+ // If this function is present, then we assume it's v9
70
+ try {
71
+ const { shouldUseFlatConfig } = require ( 'eslint/use-at-your-own-risk' ) ;
72
+ isUsingFlatConfig = shouldUseFlatConfig && ESLINT_USE_FLAT_CONFIG !== 'false' ;
73
+ } catch ( _ ) {
74
+ // We don't want to throw here, since we only want to update the
75
+ // boolean if the function is available.
76
+ }
77
+
78
+ const enumerator = new FileEnumerator ( {
62
79
extensions,
63
80
} ) ;
64
81
65
- return Array . from (
66
- e . iterateFiles ( src ) ,
67
- ( { filePath, ignored } ) => ( { filename : filePath , ignored } ) ,
68
- ) ;
82
+ try {
83
+ return Array . from (
84
+ enumerator . iterateFiles ( src ) ,
85
+ ( { filePath, ignored } ) => ( { filename : filePath , ignored } ) ,
86
+ ) ;
87
+ } catch ( e ) {
88
+ // If we're using flat config, and FileEnumerator throws due to a lack of eslintrc,
89
+ // then we want to throw an error so that the user knows about this rule's reliance on
90
+ // the legacy config.
91
+ if (
92
+ isUsingFlatConfig
93
+ && e . message . includes ( 'No ESLint configuration found' )
94
+ ) {
95
+ throw new Error ( `
96
+ Due to the exclusion of certain internal ESLint APIs when using flat config,
97
+ the import/no-unused-modules rule requires an .eslintrc file to know which
98
+ files to ignore (even when using flat config).
99
+ The .eslintrc file only needs to contain "ignorePatterns", or can be empty if
100
+ you do not want to ignore any files.
101
+
102
+ See https://github.com/import-js/eslint-plugin-import/issues/3079
103
+ for additional context.
104
+ ` ) ;
105
+ }
106
+ // If this isn't the case, then we'll just let the error bubble up
107
+ throw e ;
108
+ }
69
109
}
70
110
71
111
/**
@@ -107,70 +147,14 @@ function listFilesWithLegacyFunctions(src, extensions) {
107
147
}
108
148
}
109
149
110
- /**
111
- * Given a source root and list of supported extensions, use fsWalk and the
112
- * new `eslint` `context.session` api to build the list of files we want to operate on
113
- * @param {string[] } srcPaths array of source paths (for flat config this should just be a singular root (e.g. cwd))
114
- * @param {string[] } extensions list of supported extensions
115
- * @param {{ isDirectoryIgnored: (path: string) => boolean, isFileIgnored: (path: string) => boolean } } session eslint context session object
116
- * @returns {string[] } list of files to operate on
117
- */
118
- function listFilesWithModernApi ( srcPaths , extensions , session ) {
119
- /** @type {string[] } */
120
- const files = [ ] ;
121
-
122
- for ( let i = 0 ; i < srcPaths . length ; i ++ ) {
123
- const src = srcPaths [ i ] ;
124
- // Use walkSync along with the new session api to gather the list of files
125
- const entries = walkSync ( src , {
126
- deepFilter ( entry ) {
127
- const fullEntryPath = resolvePath ( src , entry . path ) ;
128
-
129
- // Include the directory if it's not marked as ignore by eslint
130
- return ! session . isDirectoryIgnored ( fullEntryPath ) ;
131
- } ,
132
- entryFilter ( entry ) {
133
- const fullEntryPath = resolvePath ( src , entry . path ) ;
134
-
135
- // Include the file if it's not marked as ignore by eslint and its extension is included in our list
136
- return (
137
- ! session . isFileIgnored ( fullEntryPath )
138
- && extensions . find ( ( extension ) => entry . path . endsWith ( extension ) )
139
- ) ;
140
- } ,
141
- } ) ;
142
-
143
- // Filter out directories and map entries to their paths
144
- files . push (
145
- ...entries
146
- . filter ( ( entry ) => ! entry . dirent . isDirectory ( ) )
147
- . map ( ( entry ) => entry . path ) ,
148
- ) ;
149
- }
150
- return files ;
151
- }
152
-
153
150
/**
154
151
* Given a src pattern and list of supported extensions, return a list of files to process
155
152
* with this rule.
156
153
* @param {string } src - file, directory, or glob pattern of files to act on
157
154
* @param {string[] } extensions - list of supported file extensions
158
- * @param {import('eslint').Rule.RuleContext } context - the eslint context object
159
155
* @returns {string[] | { filename: string, ignored: boolean }[] } the list of files that this rule will evaluate.
160
156
*/
161
- function listFilesToProcess ( src , extensions , context ) {
162
- // If the context object has the new session functions, then prefer those
163
- // Otherwise, fallback to using the deprecated `FileEnumerator` for legacy support.
164
- // https://github.com/eslint/eslint/issues/18087
165
- if (
166
- context . session
167
- && context . session . isFileIgnored
168
- && context . session . isDirectoryIgnored
169
- ) {
170
- return listFilesWithModernApi ( src , extensions , context . session ) ;
171
- }
172
-
173
- // Fallback to og FileEnumerator
157
+ function listFilesToProcess ( src , extensions ) {
174
158
const FileEnumerator = requireFileEnumerator ( ) ;
175
159
176
160
// If we got the FileEnumerator, then let's go with that
@@ -295,10 +279,10 @@ const isNodeModule = (path) => (/\/(node_modules)\//).test(path);
295
279
function resolveFiles ( src , ignoreExports , context ) {
296
280
const extensions = Array . from ( getFileExtensions ( context . settings ) ) ;
297
281
298
- const srcFileList = listFilesToProcess ( src , extensions , context ) ;
282
+ const srcFileList = listFilesToProcess ( src , extensions ) ;
299
283
300
284
// prepare list of ignored files
301
- const ignoredFilesList = listFilesToProcess ( ignoreExports , extensions , context ) ;
285
+ const ignoredFilesList = listFilesToProcess ( ignoreExports , extensions ) ;
302
286
303
287
// The modern api will return a list of file paths, rather than an object
304
288
if ( ignoredFilesList . length && typeof ignoredFilesList [ 0 ] === 'string' ) {
0 commit comments