async function processProject()

in tsc/src/main.ts [385:568]


async function processProject(pclOrOptions: ts.ParsedCommandLine | ConfigOptions, emitter: EmitterContext, typingsInstaller: TypingsInstaller, dataManager: DataManager, importMonikers: ImportMonikers, exportMonikers: ExportMonikers | undefined, options: ProcessProjectOptions): Promise<ProjectInfo | number> {
	let config: ts.ParsedCommandLine;
	let configFilePath: string | undefined;
	let key: string;
	if (ConfigOptions.is(pclOrOptions)) {
		if (pclOrOptions.configFilePath === undefined) {
			console.error(`No config file path available although --config is used.`);
			return -1;
		}
		configFilePath = pclOrOptions.configFilePath;
		key = configFilePath ?? makeKey(pclOrOptions);
		if (options.processed.has(key)) {
			return options.processed.get(key)!;
		}
		config = parseConfigFileContent(pclOrOptions, path.dirname(configFilePath));
	} else {
		config = pclOrOptions;
		configFilePath = tss.CompileOptions.getConfigFilePath(config.options);
		key = configFilePath ?? makeKey(config);
		if (options.processed.has(key)) {
			return options.processed.get(key)!;
		}
		if (configFilePath && !ts.sys.fileExists(configFilePath)) {
			console.error(`Project configuration file ${configFilePath} does not exist`);
			return 1;
		}

		// we have a config file path that came from a -p option. Load the file.
		if (configFilePath && config.options.project) {
			config = loadConfigFile(configFilePath);
		}
	}

	// Check if we need to do type acquisition
	if (options.typeAcquisition && (config.typeAcquisition === undefined || !!config.typeAcquisition.enable)) {
		const projectRoot = options.workspaceRoot;
		if (config.options.types !== undefined) {
			const start = configFilePath !== undefined ? configFilePath : process.cwd();
			await typingsInstaller.installTypings(projectRoot, start, config.options.types);
		} else {
			await typingsInstaller.guessTypings(projectRoot, configFilePath !== undefined ? path.dirname(configFilePath) : process.cwd());
		}
	}

	// See if we need to setup a new Export moniker manager.
	if (configFilePath !== undefined && options.packageInfo !== undefined) {
		const packageFile = options.packageInfo.get(configFilePath);
		if (packageFile !== undefined) {
			const packageJson = PackageJson.read(packageFile);
			if (packageJson !== undefined) {
				exportMonikers = new ExportMonikers(emitter, options.workspaceRoot, packageJson);
			}
		}
	}

	// Bind all symbols
	let scriptSnapshots: Map<string, ts.IScriptSnapshot> = new Map();
	const host: ts.LanguageServiceHost = {
		getScriptFileNames: () => {
			return config.fileNames;
		},
		getCompilationSettings: () => {
			return config.options;
		},
		getProjectReferences: () => {
			return config.projectReferences;
		},
		getScriptVersion: (_fileName: string): string => {
			// The files are immutable.
			return '0';
		},
		// The project is immutable
		getProjectVersion: () => '0',
		getScriptSnapshot: (fileName: string): ts.IScriptSnapshot | undefined => {
			let result: ts.IScriptSnapshot | undefined = scriptSnapshots.get(fileName);
			if (result === undefined) {
				let content: string | undefined = options.files !== undefined ? options.files.get(fileName) : undefined;
				if (content === undefined && ts.sys.fileExists(fileName)) {
					content = ts.sys.readFile(fileName);
				}
				if (content === undefined) {
					return undefined;
				}
				result = ts.ScriptSnapshot.fromString(content);
				scriptSnapshots.set(fileName, result);
			}
			return result;
		},
		getCurrentDirectory: () => {
			if (configFilePath !== undefined) {
				return path.dirname(configFilePath);
			} else {
				return process.cwd();
			}
		},
		getDefaultLibFileName: (options) => {
			// We need to return the path since the language service needs
			// to know the full path and not only the name which is return
			// from ts.getDefaultLibFileName
			return ts.getDefaultLibFilePath(options);
		},
		directoryExists: ts.sys.directoryExists,
		getDirectories: ts.sys.getDirectories,
		fileExists: ts.sys.fileExists,
		readFile: ts.sys.readFile,
		readDirectory: ts.sys.readDirectory,
		// this is necessary to make source references work.
		realpath: ts.sys.realpath
	};

	tss.LanguageServiceHost.useSourceOfProjectReferenceRedirect(host, () => {
		return !config.options.disableSourceOfProjectReferenceRedirect;
	});

	const languageService = ts.createLanguageService(host);
	let program = languageService.getProgram();
	if (program === undefined) {
		console.error('Couldn\'t create language service with underlying program.');
		process.exitCode = -1;
		return -1;
	}
	const dependsOn: ProjectInfo[] = [];
	const references = options.noProjectReferences ? undefined : program.getResolvedProjectReferences();
	if (references) {
		for (let reference of references) {
			if (reference) {
				const result = await processProject(reference.commandLine, emitter, typingsInstaller, dataManager, importMonikers, exportMonikers, options);
				if (typeof result === 'number') {
					return result;
				}
				dependsOn.push(result);
			}
		}
	}
	if ((!references || references.length === 0) && config.fileNames.length === 0) {
		console.error(`No input files specified.`);
		return 1;
	}
	// Re-fetch the program to synchronize host data after the dependent project
	// has been processed.
	program = languageService.getProgram()!;
	program.getTypeChecker();
	const level: number = options.processed.size;
	let projectName: string | undefined;
	if (options.projectName !== undefined && level === 0 && (!references || references.length === 0)) {
		projectName = options.projectName;
	}
	if (projectName === undefined && configFilePath !== undefined) {
		projectName = path.basename(path.dirname(configFilePath));
	}
	if (projectName === undefined) {
		if (options.projectName !== undefined) {
			projectName = `${options.projectName}/${level + 1}`;
		} else {
			projectName =`${path.basename(options.workspaceRoot)}/${level + 1}`;
		}
	}
	if (projectName === undefined) {
		console.error(`No project name provided.`);
		return 1;
	}

	const packageJsonFile: string | undefined = options.packageInfo === undefined
		? undefined
		: typeof options.packageInfo === 'string'
			? options.packageInfo
			: configFilePath !== undefined ? options.packageInfo.get(configFilePath) : undefined;

	const lsifOptions: LSIFOptions = {
		workspaceRoot: options.workspaceRoot,
		projectName: projectName,
		tsConfigFile: configFilePath,
		packageJsonFile: packageJsonFile,
		stdout: options.stdout,
		logger: options.reporter,
		dataMode: options.dataMode,
	};

	const result = await lsif(emitter, languageService, dataManager, importMonikers, exportMonikers, dependsOn, lsifOptions);
	if (typeof result !== 'number') {
		options.processed.set(key, result);
	}
	return result;
}