config-loader.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. /**
  2. * @fileoverview Utility to load config files
  3. * @author Nicholas C. Zakas
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const path = require("node:path");
  10. const fs = require("node:fs/promises");
  11. const findUp = require("find-up");
  12. const { pathToFileURL } = require("node:url");
  13. const debug = require("debug")("eslint:config-loader");
  14. const { FlatConfigArray } = require("../config/flat-config-array");
  15. //-----------------------------------------------------------------------------
  16. // Types
  17. //-----------------------------------------------------------------------------
  18. /**
  19. * @typedef {import("../shared/types").FlatConfigObject} FlatConfigObject
  20. * @typedef {import("../shared/types").FlatConfigArray} FlatConfigArray
  21. * @typedef {Object} ConfigLoaderOptions
  22. * @property {string|false|undefined} configFile The path to the config file to use.
  23. * @property {string} cwd The current working directory.
  24. * @property {boolean} ignoreEnabled Indicates if ignore patterns should be honored.
  25. * @property {FlatConfigArray} [baseConfig] The base config to use.
  26. * @property {Array<FlatConfigObject>} [defaultConfigs] The default configs to use.
  27. * @property {Array<string>} [ignorePatterns] The ignore patterns to use.
  28. * @property {FlatConfigObject|Array<FlatConfigObject>} overrideConfig The override config to use.
  29. * @property {boolean} allowTS Indicates if TypeScript configuration files are allowed.
  30. */
  31. //------------------------------------------------------------------------------
  32. // Helpers
  33. //------------------------------------------------------------------------------
  34. const FLAT_CONFIG_FILENAMES = [
  35. "eslint.config.js",
  36. "eslint.config.mjs",
  37. "eslint.config.cjs"
  38. ];
  39. const TS_FLAT_CONFIG_FILENAMES = [
  40. "eslint.config.ts",
  41. "eslint.config.mts",
  42. "eslint.config.cts"
  43. ];
  44. const importedConfigFileModificationTime = new Map();
  45. /**
  46. * Asserts that the given file path is valid.
  47. * @param {string} filePath The file path to check.
  48. * @returns {void}
  49. * @throws {Error} If `filePath` is not a non-empty string.
  50. */
  51. function assertValidFilePath(filePath) {
  52. if (!filePath || typeof filePath !== "string") {
  53. throw new Error("'filePath' must be a non-empty string");
  54. }
  55. }
  56. /**
  57. * Asserts that a configuration exists. A configuration exists if any
  58. * of the following are true:
  59. * - `configFilePath` is defined.
  60. * - `useConfigFile` is `false`.
  61. * @param {string|undefined} configFilePath The path to the config file.
  62. * @param {ConfigLoaderOptions} loaderOptions The options to use when loading configuration files.
  63. * @returns {void}
  64. * @throws {Error} If no configuration exists.
  65. */
  66. function assertConfigurationExists(configFilePath, loaderOptions) {
  67. const {
  68. configFile: useConfigFile
  69. } = loaderOptions;
  70. if (!configFilePath && useConfigFile !== false) {
  71. const error = new Error("Could not find config file.");
  72. error.messageTemplate = "config-file-missing";
  73. throw error;
  74. }
  75. }
  76. /**
  77. * Check if the file is a TypeScript file.
  78. * @param {string} filePath The file path to check.
  79. * @returns {boolean} `true` if the file is a TypeScript file, `false` if it's not.
  80. */
  81. function isFileTS(filePath) {
  82. const fileExtension = path.extname(filePath);
  83. return /^\.[mc]?ts$/u.test(fileExtension);
  84. }
  85. /**
  86. * Check if ESLint is running in Bun.
  87. * @returns {boolean} `true` if the ESLint is running Bun, `false` if it's not.
  88. */
  89. function isRunningInBun() {
  90. return !!globalThis.Bun;
  91. }
  92. /**
  93. * Check if ESLint is running in Deno.
  94. * @returns {boolean} `true` if the ESLint is running in Deno, `false` if it's not.
  95. */
  96. function isRunningInDeno() {
  97. return !!globalThis.Deno;
  98. }
  99. /**
  100. * Load the config array from the given filename.
  101. * @param {string} filePath The filename to load from.
  102. * @param {boolean} allowTS Indicates if TypeScript configuration files are allowed.
  103. * @returns {Promise<any>} The config loaded from the config file.
  104. */
  105. async function loadConfigFile(filePath, allowTS) {
  106. debug(`Loading config from ${filePath}`);
  107. const fileURL = pathToFileURL(filePath);
  108. debug(`Config file URL is ${fileURL}`);
  109. const mtime = (await fs.stat(filePath)).mtime.getTime();
  110. /*
  111. * Append a query with the config file's modification time (`mtime`) in order
  112. * to import the current version of the config file. Without the query, `import()` would
  113. * cache the config file module by the pathname only, and then always return
  114. * the same version (the one that was actual when the module was imported for the first time).
  115. *
  116. * This ensures that the config file module is loaded and executed again
  117. * if it has been changed since the last time it was imported.
  118. * If it hasn't been changed, `import()` will just return the cached version.
  119. *
  120. * Note that we should not overuse queries (e.g., by appending the current time
  121. * to always reload the config file module) as that could cause memory leaks
  122. * because entries are never removed from the import cache.
  123. */
  124. fileURL.searchParams.append("mtime", mtime);
  125. /*
  126. * With queries, we can bypass the import cache. However, when import-ing a CJS module,
  127. * Node.js uses the require infrastructure under the hood. That includes the require cache,
  128. * which caches the config file module by its file path (queries have no effect).
  129. * Therefore, we also need to clear the require cache before importing the config file module.
  130. * In order to get the same behavior with ESM and CJS config files, in particular - to reload
  131. * the config file only if it has been changed, we track file modification times and clear
  132. * the require cache only if the file has been changed.
  133. */
  134. if (importedConfigFileModificationTime.get(filePath) !== mtime) {
  135. delete require.cache[filePath];
  136. }
  137. const isTS = isFileTS(filePath);
  138. const isBun = isRunningInBun();
  139. const isDeno = isRunningInDeno();
  140. /*
  141. * If we are dealing with a TypeScript file, then we need to use `jiti` to load it
  142. * in Node.js. Deno and Bun both allow native importing of TypeScript files.
  143. *
  144. * When Node.js supports native TypeScript imports, we can remove this check.
  145. */
  146. if (allowTS && isTS && !isDeno && !isBun) {
  147. // eslint-disable-next-line no-use-before-define -- `ConfigLoader.loadJiti` can be overwritten for testing
  148. const { createJiti } = await ConfigLoader.loadJiti().catch(() => {
  149. throw new Error("The 'jiti' library is required for loading TypeScript configuration files. Make sure to install it.");
  150. });
  151. // `createJiti` was added in jiti v2.
  152. if (typeof createJiti !== "function") {
  153. throw new Error("You are using an outdated version of the 'jiti' library. Please update to the latest version of 'jiti' to ensure compatibility and access to the latest features.");
  154. }
  155. /*
  156. * Disabling `moduleCache` allows us to reload a
  157. * config file when the last modified timestamp changes.
  158. */
  159. const jiti = createJiti(__filename, { moduleCache: false, interopDefault: false });
  160. const config = await jiti.import(fileURL.href);
  161. importedConfigFileModificationTime.set(filePath, mtime);
  162. return config?.default ?? config;
  163. }
  164. // fallback to normal runtime behavior
  165. const config = (await import(fileURL)).default;
  166. importedConfigFileModificationTime.set(filePath, mtime);
  167. return config;
  168. }
  169. /**
  170. * Calculates the config array for this run based on inputs.
  171. * @param {string} configFilePath The absolute path to the config file to use if not overridden.
  172. * @param {string} basePath The base path to use for relative paths in the config file.
  173. * @param {ConfigLoaderOptions} options The options to use when loading configuration files.
  174. * @returns {Promise<FlatConfigArray>} The config array for `eslint`.
  175. */
  176. async function calculateConfigArray(configFilePath, basePath, options) {
  177. const {
  178. cwd,
  179. baseConfig,
  180. ignoreEnabled,
  181. ignorePatterns,
  182. overrideConfig,
  183. defaultConfigs = [],
  184. allowTS
  185. } = options;
  186. debug(`Calculating config array from config file ${configFilePath} and base path ${basePath}`);
  187. const configs = new FlatConfigArray(baseConfig || [], { basePath, shouldIgnore: ignoreEnabled });
  188. // load config file
  189. if (configFilePath) {
  190. debug(`Loading config file ${configFilePath}`);
  191. const fileConfig = await loadConfigFile(configFilePath, allowTS);
  192. if (Array.isArray(fileConfig)) {
  193. configs.push(...fileConfig);
  194. } else {
  195. configs.push(fileConfig);
  196. }
  197. }
  198. // add in any configured defaults
  199. configs.push(...defaultConfigs);
  200. // append command line ignore patterns
  201. if (ignorePatterns && ignorePatterns.length > 0) {
  202. let relativeIgnorePatterns;
  203. /*
  204. * If the config file basePath is different than the cwd, then
  205. * the ignore patterns won't work correctly. Here, we adjust the
  206. * ignore pattern to include the correct relative path. Patterns
  207. * passed as `ignorePatterns` are relative to the cwd, whereas
  208. * the config file basePath can be an ancestor of the cwd.
  209. */
  210. if (basePath === cwd) {
  211. relativeIgnorePatterns = ignorePatterns;
  212. } else {
  213. // relative path must only have Unix-style separators
  214. const relativeIgnorePath = path.relative(basePath, cwd).replace(/\\/gu, "/");
  215. relativeIgnorePatterns = ignorePatterns.map(pattern => {
  216. const negated = pattern.startsWith("!");
  217. const basePattern = negated ? pattern.slice(1) : pattern;
  218. return (negated ? "!" : "") +
  219. path.posix.join(relativeIgnorePath, basePattern);
  220. });
  221. }
  222. /*
  223. * Ignore patterns are added to the end of the config array
  224. * so they can override default ignores.
  225. */
  226. configs.push({
  227. ignores: relativeIgnorePatterns
  228. });
  229. }
  230. if (overrideConfig) {
  231. if (Array.isArray(overrideConfig)) {
  232. configs.push(...overrideConfig);
  233. } else {
  234. configs.push(overrideConfig);
  235. }
  236. }
  237. await configs.normalize();
  238. return configs;
  239. }
  240. //-----------------------------------------------------------------------------
  241. // Exports
  242. //-----------------------------------------------------------------------------
  243. /**
  244. * Encapsulates the loading and caching of configuration files when looking up
  245. * from the file being linted.
  246. */
  247. class ConfigLoader {
  248. /**
  249. * Map of config file paths to the config arrays for those directories.
  250. * @type {Map<string, FlatConfigArray>}
  251. */
  252. #configArrays = new Map();
  253. /**
  254. * Map of absolute directory names to the config file paths for those directories.
  255. * @type {Map<string, {configFilePath:string,basePath:string}>}
  256. */
  257. #configFilePaths = new Map();
  258. /**
  259. * The options to use when loading configuration files.
  260. * @type {ConfigLoaderOptions}
  261. */
  262. #options;
  263. /**
  264. * Creates a new instance.
  265. * @param {ConfigLoaderOptions} options The options to use when loading configuration files.
  266. */
  267. constructor(options) {
  268. this.#options = options;
  269. }
  270. /**
  271. * Determines which config file to use. This is determined by seeing if an
  272. * override config file was specified, and if so, using it; otherwise, as long
  273. * as override config file is not explicitly set to `false`, it will search
  274. * upwards from `fromDirectory` for a file named `eslint.config.js`.
  275. * @param {string} fromDirectory The directory from which to start searching.
  276. * @returns {Promise<{configFilePath:string|undefined,basePath:string}>} Location information for
  277. * the config file.
  278. */
  279. async #locateConfigFileToUse(fromDirectory) {
  280. // check cache first
  281. if (this.#configFilePaths.has(fromDirectory)) {
  282. return this.#configFilePaths.get(fromDirectory);
  283. }
  284. const configFilenames = this.#options.allowTS
  285. ? [...FLAT_CONFIG_FILENAMES, ...TS_FLAT_CONFIG_FILENAMES]
  286. : FLAT_CONFIG_FILENAMES;
  287. // determine where to load config file from
  288. let configFilePath;
  289. const {
  290. cwd,
  291. configFile: useConfigFile
  292. } = this.#options;
  293. let basePath = cwd;
  294. if (typeof useConfigFile === "string") {
  295. debug(`Override config file path is ${useConfigFile}`);
  296. configFilePath = path.resolve(cwd, useConfigFile);
  297. basePath = cwd;
  298. } else if (useConfigFile !== false) {
  299. debug("Searching for eslint.config.js");
  300. configFilePath = await findUp(
  301. configFilenames,
  302. { cwd: fromDirectory }
  303. );
  304. if (configFilePath) {
  305. basePath = path.dirname(configFilePath);
  306. }
  307. }
  308. // cache the result
  309. this.#configFilePaths.set(fromDirectory, { configFilePath, basePath });
  310. return {
  311. configFilePath,
  312. basePath
  313. };
  314. }
  315. /**
  316. * Calculates the config array for this run based on inputs.
  317. * @param {string} configFilePath The absolute path to the config file to use if not overridden.
  318. * @param {string} basePath The base path to use for relative paths in the config file.
  319. * @returns {Promise<FlatConfigArray>} The config array for `eslint`.
  320. */
  321. async #calculateConfigArray(configFilePath, basePath) {
  322. // check for cached version first
  323. if (this.#configArrays.has(configFilePath)) {
  324. return this.#configArrays.get(configFilePath);
  325. }
  326. const configs = await calculateConfigArray(configFilePath, basePath, this.#options);
  327. // cache the config array for this instance
  328. this.#configArrays.set(configFilePath, configs);
  329. return configs;
  330. }
  331. /**
  332. * Returns the config file path for the given directory or file. This will either use
  333. * the override config file that was specified in the constructor options or
  334. * search for a config file from the directory.
  335. * @param {string} fileOrDirPath The file or directory path to get the config file path for.
  336. * @returns {Promise<string|undefined>} The config file path or `undefined` if not found.
  337. * @throws {Error} If `fileOrDirPath` is not a non-empty string.
  338. * @throws {Error} If `fileOrDirPath` is not an absolute path.
  339. */
  340. async findConfigFileForPath(fileOrDirPath) {
  341. assertValidFilePath(fileOrDirPath);
  342. const absoluteDirPath = path.resolve(this.#options.cwd, path.dirname(fileOrDirPath));
  343. const { configFilePath } = await this.#locateConfigFileToUse(absoluteDirPath);
  344. return configFilePath;
  345. }
  346. /**
  347. * Returns a configuration object for the given file based on the CLI options.
  348. * This is the same logic used by the ESLint CLI executable to determine
  349. * configuration for each file it processes.
  350. * @param {string} filePath The path of the file or directory to retrieve config for.
  351. * @returns {Promise<ConfigData|undefined>} A configuration object for the file
  352. * or `undefined` if there is no configuration data for the file.
  353. * @throws {Error} If no configuration for `filePath` exists.
  354. */
  355. async loadConfigArrayForFile(filePath) {
  356. assertValidFilePath(filePath);
  357. debug(`Calculating config for file ${filePath}`);
  358. const configFilePath = await this.findConfigFileForPath(filePath);
  359. assertConfigurationExists(configFilePath, this.#options);
  360. return this.loadConfigArrayForDirectory(filePath);
  361. }
  362. /**
  363. * Returns a configuration object for the given directory based on the CLI options.
  364. * This is the same logic used by the ESLint CLI executable to determine
  365. * configuration for each file it processes.
  366. * @param {string} dirPath The path of the directory to retrieve config for.
  367. * @returns {Promise<ConfigData|undefined>} A configuration object for the directory
  368. * or `undefined` if there is no configuration data for the directory.
  369. */
  370. async loadConfigArrayForDirectory(dirPath) {
  371. assertValidFilePath(dirPath);
  372. debug(`Calculating config for directory ${dirPath}`);
  373. const absoluteDirPath = path.resolve(this.#options.cwd, path.dirname(dirPath));
  374. const { configFilePath, basePath } = await this.#locateConfigFileToUse(absoluteDirPath);
  375. debug(`Using config file ${configFilePath} and base path ${basePath}`);
  376. return this.#calculateConfigArray(configFilePath, basePath);
  377. }
  378. /**
  379. * Returns a configuration array for the given file based on the CLI options.
  380. * This is a synchronous operation and does not read any files from disk. It's
  381. * intended to be used in locations where we know the config file has already
  382. * been loaded and we just need to get the configuration for a file.
  383. * @param {string} filePath The path of the file to retrieve a config object for.
  384. * @returns {ConfigData|undefined} A configuration object for the file
  385. * or `undefined` if there is no configuration data for the file.
  386. * @throws {Error} If `filePath` is not a non-empty string.
  387. * @throws {Error} If `filePath` is not an absolute path.
  388. * @throws {Error} If the config file was not already loaded.
  389. */
  390. getCachedConfigArrayForFile(filePath) {
  391. assertValidFilePath(filePath);
  392. debug(`Looking up cached config for ${filePath}`);
  393. return this.getCachedConfigArrayForPath(path.dirname(filePath));
  394. }
  395. /**
  396. * Returns a configuration array for the given directory based on the CLI options.
  397. * This is a synchronous operation and does not read any files from disk. It's
  398. * intended to be used in locations where we know the config file has already
  399. * been loaded and we just need to get the configuration for a file.
  400. * @param {string} fileOrDirPath The path of the directory to retrieve a config object for.
  401. * @returns {ConfigData|undefined} A configuration object for the directory
  402. * or `undefined` if there is no configuration data for the directory.
  403. * @throws {Error} If `dirPath` is not a non-empty string.
  404. * @throws {Error} If `dirPath` is not an absolute path.
  405. * @throws {Error} If the config file was not already loaded.
  406. */
  407. getCachedConfigArrayForPath(fileOrDirPath) {
  408. assertValidFilePath(fileOrDirPath);
  409. debug(`Looking up cached config for ${fileOrDirPath}`);
  410. const absoluteDirPath = path.resolve(this.#options.cwd, fileOrDirPath);
  411. if (!this.#configFilePaths.has(absoluteDirPath)) {
  412. throw new Error(`Could not find config file for ${fileOrDirPath}`);
  413. }
  414. const { configFilePath } = this.#configFilePaths.get(absoluteDirPath);
  415. return this.#configArrays.get(configFilePath);
  416. }
  417. /**
  418. * Used to import the jiti dependency. This method is exposed internally for testing purposes.
  419. * @returns {Promise<Record<string, unknown>>} A promise that fulfills with a module object
  420. * or rejects with an error if jiti is not found.
  421. */
  422. static loadJiti() {
  423. return import("jiti");
  424. }
  425. }
  426. /**
  427. * Encapsulates the loading and caching of configuration files when looking up
  428. * from the current working directory.
  429. */
  430. class LegacyConfigLoader extends ConfigLoader {
  431. /**
  432. * The options to use when loading configuration files.
  433. * @type {ConfigLoaderOptions}
  434. */
  435. #options;
  436. /**
  437. * The cached config file path for this instance.
  438. * @type {{configFilePath:string,basePath:string}|undefined}
  439. */
  440. #configFilePath;
  441. /**
  442. * The cached config array for this instance.
  443. * @type {FlatConfigArray}
  444. */
  445. #configArray;
  446. /**
  447. * Creates a new instance.
  448. * @param {ConfigLoaderOptions} options The options to use when loading configuration files.
  449. */
  450. constructor(options) {
  451. super(options);
  452. this.#options = options;
  453. }
  454. /**
  455. * Determines which config file to use. This is determined by seeing if an
  456. * override config file was specified, and if so, using it; otherwise, as long
  457. * as override config file is not explicitly set to `false`, it will search
  458. * upwards from the cwd for a file named `eslint.config.js`.
  459. * @returns {Promise<{configFilePath:string|undefined,basePath:string}>} Location information for
  460. * the config file.
  461. */
  462. async #locateConfigFileToUse() {
  463. // check cache first
  464. if (this.#configFilePath) {
  465. return this.#configFilePath;
  466. }
  467. const configFilenames = this.#options.allowTS
  468. ? [...FLAT_CONFIG_FILENAMES, ...TS_FLAT_CONFIG_FILENAMES]
  469. : FLAT_CONFIG_FILENAMES;
  470. // determine where to load config file from
  471. let configFilePath;
  472. const {
  473. cwd,
  474. configFile: useConfigFile
  475. } = this.#options;
  476. let basePath = cwd;
  477. if (typeof useConfigFile === "string") {
  478. debug(`[Legacy]: Override config file path is ${useConfigFile}`);
  479. configFilePath = path.resolve(cwd, useConfigFile);
  480. basePath = cwd;
  481. } else if (useConfigFile !== false) {
  482. debug("[Legacy]: Searching for eslint.config.js");
  483. configFilePath = await findUp(
  484. configFilenames,
  485. { cwd }
  486. );
  487. if (configFilePath) {
  488. basePath = path.dirname(configFilePath);
  489. }
  490. }
  491. // cache the result
  492. this.#configFilePath = { configFilePath, basePath };
  493. return {
  494. configFilePath,
  495. basePath
  496. };
  497. }
  498. /**
  499. * Calculates the config array for this run based on inputs.
  500. * @param {string} configFilePath The absolute path to the config file to use if not overridden.
  501. * @param {string} basePath The base path to use for relative paths in the config file.
  502. * @returns {Promise<FlatConfigArray>} The config array for `eslint`.
  503. */
  504. async #calculateConfigArray(configFilePath, basePath) {
  505. // check for cached version first
  506. if (this.#configArray) {
  507. return this.#configArray;
  508. }
  509. const configs = await calculateConfigArray(configFilePath, basePath, this.#options);
  510. // cache the config array for this instance
  511. this.#configArray = configs;
  512. return configs;
  513. }
  514. /**
  515. * Returns the config file path for the given directory. This will either use
  516. * the override config file that was specified in the constructor options or
  517. * search for a config file from the directory of the file being linted.
  518. * @param {string} dirPath The directory path to get the config file path for.
  519. * @returns {Promise<string|undefined>} The config file path or `undefined` if not found.
  520. * @throws {Error} If `fileOrDirPath` is not a non-empty string.
  521. * @throws {Error} If `fileOrDirPath` is not an absolute path.
  522. */
  523. async findConfigFileForPath(dirPath) {
  524. assertValidFilePath(dirPath);
  525. const { configFilePath } = await this.#locateConfigFileToUse();
  526. return configFilePath;
  527. }
  528. /**
  529. * Returns a configuration object for the given file based on the CLI options.
  530. * This is the same logic used by the ESLint CLI executable to determine
  531. * configuration for each file it processes.
  532. * @param {string} dirPath The path of the directory to retrieve config for.
  533. * @returns {Promise<ConfigData|undefined>} A configuration object for the file
  534. * or `undefined` if there is no configuration data for the file.
  535. */
  536. async loadConfigArrayForDirectory(dirPath) {
  537. assertValidFilePath(dirPath);
  538. debug(`[Legacy]: Calculating config for ${dirPath}`);
  539. const { configFilePath, basePath } = await this.#locateConfigFileToUse();
  540. debug(`[Legacy]: Using config file ${configFilePath} and base path ${basePath}`);
  541. return this.#calculateConfigArray(configFilePath, basePath);
  542. }
  543. /**
  544. * Returns a configuration array for the given directory based on the CLI options.
  545. * This is a synchronous operation and does not read any files from disk. It's
  546. * intended to be used in locations where we know the config file has already
  547. * been loaded and we just need to get the configuration for a file.
  548. * @param {string} dirPath The path of the directory to retrieve a config object for.
  549. * @returns {ConfigData|undefined} A configuration object for the file
  550. * or `undefined` if there is no configuration data for the file.
  551. * @throws {Error} If `dirPath` is not a non-empty string.
  552. * @throws {Error} If `dirPath` is not an absolute path.
  553. * @throws {Error} If the config file was not already loaded.
  554. */
  555. getCachedConfigArrayForPath(dirPath) {
  556. assertValidFilePath(dirPath);
  557. debug(`[Legacy]: Looking up cached config for ${dirPath}`);
  558. if (!this.#configArray) {
  559. throw new Error(`Could not find config file for ${dirPath}`);
  560. }
  561. return this.#configArray;
  562. }
  563. }
  564. module.exports = { ConfigLoader, LegacyConfigLoader };