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;
}