mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-07-05 00:32:41 -05:00
If these are regular comments, then they won't appear in our d.ts files. But, now we are relying on an external d.ts bundler to produce our final merged, so they need to be present in the "input" d.ts files, meaning they have to be JSDoc comments. These comments only work today because all of our builds load their TS files from scratch, so they see the actual source files and their non-JSDoc comments. The comments also need to be attached to a declaration, not floating, otherwise they won't be used by api-extractor, so move them if needed.
148 lines
5.7 KiB
TypeScript
148 lines
5.7 KiB
TypeScript
import {
|
|
ApplyCodeActionCommandResult, arrayIsEqualTo, CompilerOptions, getAllowJSCompilerOption, InstallPackageOptions, Map,
|
|
noop, notImplemented, Path, returnFalse, sort, SortedReadonlyArray, TypeAcquisition,
|
|
} from "./_namespaces/ts";
|
|
import { emptyArray, Project, ProjectService } from "./_namespaces/ts.server";
|
|
|
|
export interface InstallPackageOptionsWithProject extends InstallPackageOptions {
|
|
projectName: string;
|
|
projectRootPath: Path;
|
|
}
|
|
|
|
// for backwards-compatibility
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
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 = new Map<string, 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 getAllowJSCompilerOption(opt1) !== getAllowJSCompilerOption(opt2);
|
|
}
|
|
|
|
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 = new Map<string, 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);
|
|
}
|
|
}
|