mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 16:38:05 -06:00
144 lines
5.9 KiB
TypeScript
144 lines
5.9 KiB
TypeScript
namespace ts.server {
|
|
export interface InstallPackageOptionsWithProject extends InstallPackageOptions {
|
|
projectName: string;
|
|
projectRootPath: Path;
|
|
}
|
|
|
|
// for backwards-compatibility
|
|
// eslint-disable-next-line @typescript-eslint/interface-name-prefix
|
|
export interface ITypingsInstaller {
|
|
isKnownTypesPackageName(name: string): boolean;
|
|
installPackage(options: InstallPackageOptionsWithProject): Promise<ApplyCodeActionCommandResult>;
|
|
enqueueInstallTypingsRequest(p: Project, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray<string> | undefined): void;
|
|
attach(projectService: ProjectService): void;
|
|
onProjectClosed(p: Project): void;
|
|
readonly globalTypingsCacheLocation: string | undefined;
|
|
}
|
|
|
|
export const nullTypingsInstaller: ITypingsInstaller = {
|
|
isKnownTypesPackageName: returnFalse,
|
|
// Should never be called because we never provide a types registry.
|
|
installPackage: notImplemented,
|
|
enqueueInstallTypingsRequest: noop,
|
|
attach: noop,
|
|
onProjectClosed: noop,
|
|
globalTypingsCacheLocation: undefined! // TODO: GH#18217
|
|
};
|
|
|
|
interface TypingsCacheEntry {
|
|
readonly typeAcquisition: TypeAcquisition;
|
|
readonly compilerOptions: CompilerOptions;
|
|
readonly typings: SortedReadonlyArray<string>;
|
|
readonly unresolvedImports: SortedReadonlyArray<string> | undefined;
|
|
/* mainly useful for debugging */
|
|
poisoned: boolean;
|
|
}
|
|
|
|
function setIsEqualTo(arr1: string[] | undefined, arr2: string[] | undefined): boolean {
|
|
if (arr1 === arr2) {
|
|
return true;
|
|
}
|
|
if ((arr1 || emptyArray).length === 0 && (arr2 || emptyArray).length === 0) {
|
|
return true;
|
|
}
|
|
const set: Map<boolean> = createMap<boolean>();
|
|
let unique = 0;
|
|
|
|
for (const v of arr1!) {
|
|
if (set.get(v) !== true) {
|
|
set.set(v, true);
|
|
unique++;
|
|
}
|
|
}
|
|
for (const v of arr2!) {
|
|
const isSet = set.get(v);
|
|
if (isSet === undefined) {
|
|
return false;
|
|
}
|
|
if (isSet === true) {
|
|
set.set(v, false);
|
|
unique--;
|
|
}
|
|
}
|
|
return unique === 0;
|
|
}
|
|
|
|
function typeAcquisitionChanged(opt1: TypeAcquisition, opt2: TypeAcquisition): boolean {
|
|
return opt1.enable !== opt2.enable ||
|
|
!setIsEqualTo(opt1.include, opt2.include) ||
|
|
!setIsEqualTo(opt1.exclude, opt2.exclude);
|
|
}
|
|
|
|
function compilerOptionsChanged(opt1: CompilerOptions, opt2: CompilerOptions): boolean {
|
|
// TODO: add more relevant properties
|
|
return opt1.allowJs !== opt2.allowJs;
|
|
}
|
|
|
|
function unresolvedImportsChanged(imports1: SortedReadonlyArray<string> | undefined, imports2: SortedReadonlyArray<string> | undefined): boolean {
|
|
if (imports1 === imports2) {
|
|
return false;
|
|
}
|
|
return !arrayIsEqualTo(imports1, imports2);
|
|
}
|
|
|
|
/*@internal*/
|
|
export class TypingsCache {
|
|
private readonly perProjectCache: Map<TypingsCacheEntry> = createMap<TypingsCacheEntry>();
|
|
|
|
constructor(private readonly installer: ITypingsInstaller) {
|
|
}
|
|
|
|
isKnownTypesPackageName(name: string): boolean {
|
|
return this.installer.isKnownTypesPackageName(name);
|
|
}
|
|
|
|
installPackage(options: InstallPackageOptionsWithProject): Promise<ApplyCodeActionCommandResult> {
|
|
return this.installer.installPackage(options);
|
|
}
|
|
|
|
enqueueInstallTypingsForProject(project: Project, unresolvedImports: SortedReadonlyArray<string> | undefined, forceRefresh: boolean) {
|
|
const typeAcquisition = project.getTypeAcquisition();
|
|
|
|
if (!typeAcquisition || !typeAcquisition.enable) {
|
|
return;
|
|
}
|
|
|
|
const entry = this.perProjectCache.get(project.getProjectName());
|
|
if (forceRefresh ||
|
|
!entry ||
|
|
typeAcquisitionChanged(typeAcquisition, entry.typeAcquisition) ||
|
|
compilerOptionsChanged(project.getCompilationSettings(), entry.compilerOptions) ||
|
|
unresolvedImportsChanged(unresolvedImports, entry.unresolvedImports)) {
|
|
// Note: entry is now poisoned since it does not really contain typings for a given combination of compiler options\typings options.
|
|
// instead it acts as a placeholder to prevent issuing multiple requests
|
|
this.perProjectCache.set(project.getProjectName(), {
|
|
compilerOptions: project.getCompilationSettings(),
|
|
typeAcquisition,
|
|
typings: entry ? entry.typings : emptyArray,
|
|
unresolvedImports,
|
|
poisoned: true
|
|
});
|
|
// something has been changed, issue a request to update typings
|
|
this.installer.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
|
|
}
|
|
}
|
|
|
|
updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray<string>, newTypings: string[]) {
|
|
const typings = sort(newTypings);
|
|
this.perProjectCache.set(projectName, {
|
|
compilerOptions,
|
|
typeAcquisition,
|
|
typings,
|
|
unresolvedImports,
|
|
poisoned: false
|
|
});
|
|
return !typeAcquisition || !typeAcquisition.enable ? emptyArray : typings;
|
|
}
|
|
|
|
onProjectClosed(project: Project) {
|
|
this.perProjectCache.delete(project.getProjectName());
|
|
this.installer.onProjectClosed(project);
|
|
}
|
|
}
|
|
}
|