TypeScript/src/typingsInstallerCore/typingsInstaller.ts

546 lines
26 KiB
TypeScript
Raw Normal View History

2016-08-12 20:04:43 +02:00
namespace ts.server.typingsInstaller {
2016-08-26 01:25:34 +02:00
interface NpmConfig {
devDependencies: MapLike<any>;
}
interface NpmLock {
dependencies: { [packageName: string]: { version: string } };
}
export interface Log {
isEnabled(): boolean;
writeLine(text: string): void;
}
2016-08-17 23:47:54 +02:00
const nullLog: Log = {
isEnabled: () => false,
writeLine: noop
2016-08-17 23:47:54 +02:00
};
Enable '--strictNullChecks' (#22088) * Enable '--strictNullChecks' * Fix API baselines * Make sys.getEnvironmentVariable non-nullable * make properties optional instead of using `| undefined` in thier type * reportDiagnostics should be required * Declare firstAccessor as non-nullable * Make `some` a type guard * Fix `getEnvironmentVariable` definition in tests * Pretend transformFlags are always defined * Fix one more use of sys.getEnvironmentVariable * `requiredResponse` accepts undefined, remove assertions * Mark optional properties as optional instead of using `| undefined` * Mark optional properties as optional instead of using ` | undefined` * Remove unnecessary null assertions * Put the bang on the declaration instead of every use * Make `createMapFromTemplate` require a parameter * Mark `EmitResult.emittedFiles` and `EmitResult.sourceMaps` as optional * Plumb through undefined in emitLsit and EmitExpressionList * `ElementAccessExpression.argumentExpression` can not be `undefined` * Add overloads for `writeTokenText` * Make `shouldWriteSeparatingLineTerminator` argument non-nullable * Make `synthesizedNodeStartsOnNewLine` argument required * `PropertyAssignment.initializer` cannot be undefined * Use one `!` at declaration site instead of on every use site * Capture host in a constant and avoid null assertions * Remove few more unused assertions * Update baselines * Use parameter defaults * Update baselines * Fix lint * Make Symbol#valueDeclaration and Symbol#declarations non-optional to reduce assertions * Make Node#symbol and Type#symbol non-optional to reduce assertions * Make `flags` non-nullable to reduce assertions * Convert some asserts to type guards * Make `isNonLocalAlias` a type guard * Add overload for `getSymbolOfNode` for `Declaration` * Some more `getSymbolOfNode` changes * Push undefined suppression into `typeToTypeNodeHelper` * `NodeBuilderContext.tracker` is never `undefined` * use `Debug.assertDefined` * Remove unnecessary tag * Mark `LiteralType.freshType` and `LiteralTupe.regularType` as required
2018-05-22 23:46:57 +02:00
function typingToFileName(cachePath: string, packageName: string, installTypingHost: InstallTypingHost, log: Log): string | undefined {
try {
const result = resolveModuleName(packageName, combinePaths(cachePath, "index.d.ts"), { moduleResolution: ModuleResolutionKind.NodeJs }, installTypingHost);
return result.resolvedModule && result.resolvedModule.resolvedFileName;
}
catch (e) {
if (log.isEnabled()) {
log.writeLine(`Failed to resolve ${packageName} in folder '${cachePath}': ${(e as Error).message}`);
}
return undefined;
}
}
/*@internal*/
export function installNpmPackages(npmPath: string, tsVersion: string, packageNames: string[], install: (command: string) => boolean) {
let hasError = false;
for (let remaining = packageNames.length; remaining > 0;) {
const result = getNpmCommandForInstallation(npmPath, tsVersion, packageNames, remaining);
remaining = result.remaining;
hasError = install(result.command) || hasError;
}
return hasError;
}
/*@internal*/
export function getNpmCommandForInstallation(npmPath: string, tsVersion: string, packageNames: string[], remaining: number) {
const sliceStart = packageNames.length - remaining;
let command: string, toSlice = remaining;
while (true) {
command = `${npmPath} install --ignore-scripts ${(toSlice === packageNames.length ? packageNames : packageNames.slice(sliceStart, sliceStart + toSlice)).join(" ")} --save-dev --user-agent="typesInstaller/${tsVersion}"`;
if (command.length < 8000) {
break;
}
toSlice = toSlice - Math.floor(toSlice / 2);
}
return { command, remaining: remaining - toSlice };
}
export type RequestCompletedAction = (success: boolean) => void;
interface PendingRequest {
2016-09-20 23:14:51 +02:00
requestId: number;
packageNames: string[];
2016-09-20 23:14:51 +02:00
cwd: string;
Merge release-2.1 into master (#12157) * Update LKG * Update version * Update LKG * Skip overloads with too-short function parameters If the parameter of an overload is a function and the argument is also a function, skip the overload if the parameter has fewer arguments than the argument does. That overload cannot possibly apply, and should not participate in, for example, contextual typing. Example: ```ts interface I { (a: number): void; (b: string, c): void; } declare function f(i: I): void; f((x, y) => {}); ``` This code now skips the first overload instead of considering. This was a longstanding bug but was only uncovered now that more functions expressions are context sensitive. * Test skip overloads w/too-short function params 1. Update changed baseline. 2. Add a new test with baseline. * Minor style improvements * Ignore optionality when skipping overloads * Do not use contextual signatures with too few parameters * isAritySmaller runs later: getNonGenericSignature * rewrite void-returning statements in constructors that capture result of super call (#11868) * rewrite void-returning statements in constructors that capture result of super call * linter * Update LKG * Fix emit inferred type which is a generic type-alias both fully and partially fill type parameters * Add tests and baselines * Skip trying to use alias if there is target type * Update baselines * Add diagnostics to remind adding tsconfig file for certain external project (#11932) * Add diagnostics for certain external project * Show tsconfig suggestion * fix lint error * Address pr * fix comment * Update error message * Flag for not overwrite js files by default without generating errors (#11980) * WIP * Properly naming things * refactor * apply the option to all files and check out options * Fix typo * Update LKG * lockLinter * use local registry to check if typings package exist (#12014) (#12032) use local registry to check if typings package exist * Add test for https://github.com/Microsoft/TypeScript/pull/11980 (#12027) * add test for the fix for overwrite emitting error * cr feedback * enable sending telemetry events to tsserver client (#12034) (#12051) enable sending telemetry events * Update LKG * Reuse subtree transform flags for incrementally parsed nodes (#12088) * Update LKG * Update version * Update LKG * Do not emit "use strict" when targeting es6 or higher or module kind is es2015 and the file is external module * Add tests and baselines * [Release 2.1] fix11754 global augmentation (#12133) * Exclude global augmentation from module resolution logic * Address PR: check using string literal instead of NodeFlags.globalAugmentation * Remove comment
2016-11-10 23:28:34 +01:00
onRequestCompleted: RequestCompletedAction;
}
2016-09-20 23:14:51 +02:00
function endsWith(str: string, suffix: string, caseSensitive: boolean): boolean {
const expectedPos = str.length - suffix.length;
return expectedPos >= 0 &&
(str.indexOf(suffix, expectedPos) === expectedPos ||
(!caseSensitive && compareStringsCaseInsensitive(str.substr(expectedPos), suffix) === Comparison.EqualTo));
}
function isPackageOrBowerJson(fileName: string, caseSensitive: boolean) {
return endsWith(fileName, "/package.json", caseSensitive) || endsWith(fileName, "/bower.json", caseSensitive);
}
function sameFiles(a: string, b: string, caseSensitive: boolean) {
return a === b || (!caseSensitive && compareStringsCaseInsensitive(a, b) === Comparison.EqualTo);
}
const enum ProjectWatcherType {
FileWatcher = "FileWatcher",
DirectoryWatcher = "DirectoryWatcher"
}
type ProjectWatchers = ESMap<string, FileWatcher> & { isInvoked?: boolean; };
function getDetailWatchInfo(projectName: string, watchers: ProjectWatchers) {
return `Project: ${projectName} watcher already invoked: ${watchers.isInvoked}`;
}
2016-08-12 20:04:43 +02:00
export abstract class TypingsInstaller {
private readonly packageNameToTypingLocation = new Map<string, JsTyping.CachedTyping>();
private readonly missingTypingsSet = new Set<string>();
private readonly knownCachesSet = new Set<string>();
2020-06-26 01:03:25 +02:00
private readonly projectWatchers = new Map<string, ProjectWatchers>();
private safeList: JsTyping.SafeList | undefined;
2016-09-20 23:14:51 +02:00
readonly pendingRunRequests: PendingRequest[] = [];
private readonly toCanonicalFileName: GetCanonicalFileName;
private readonly globalCachePackageJsonPath: string;
2016-09-20 23:14:51 +02:00
private installRunCount = 1;
2016-09-20 23:14:51 +02:00
private inFlightRequestCount = 0;
2016-08-16 23:21:09 +02:00
abstract readonly typesRegistry: ESMap<string, MapLike<string>>;
/*@internal*/
private readonly watchFactory: WatchFactory<string, ProjectWatchers>;
2016-09-20 23:14:51 +02:00
constructor(
2017-07-14 23:26:13 +02:00
protected readonly installTypingHost: InstallTypingHost,
private readonly globalCachePath: string,
private readonly safeListPath: Path,
2017-07-28 01:07:50 +02:00
private readonly typesMapLocation: Path,
2017-07-14 23:26:13 +02:00
private readonly throttleLimit: number,
2016-09-20 23:14:51 +02:00
protected readonly log = nullLog) {
this.toCanonicalFileName = createGetCanonicalFileName(installTypingHost.useCaseSensitiveFileNames);
this.globalCachePackageJsonPath = combinePaths(globalCachePath, "package.json");
const isLoggingEnabled = this.log.isEnabled();
if (isLoggingEnabled) {
2017-07-28 01:07:50 +02:00
this.log.writeLine(`Global cache location '${globalCachePath}', safe file path '${safeListPath}', types map path ${typesMapLocation}`);
}
this.watchFactory = getWatchFactory(this.installTypingHost as WatchFactoryHost, isLoggingEnabled ? WatchLogLevel.Verbose : WatchLogLevel.None, s => this.log.writeLine(s), getDetailWatchInfo);
this.processCacheLocation(this.globalCachePath);
2016-08-12 20:04:43 +02:00
}
2016-08-16 23:21:09 +02:00
closeProject(req: CloseProject) {
this.closeWatchers(req.projectName);
}
private closeWatchers(projectName: string): void {
2016-08-16 23:21:09 +02:00
if (this.log.isEnabled()) {
this.log.writeLine(`Closing file watchers for project '${projectName}'`);
}
2016-12-05 23:13:32 +01:00
const watchers = this.projectWatchers.get(projectName);
2016-08-16 23:21:09 +02:00
if (!watchers) {
if (this.log.isEnabled()) {
this.log.writeLine(`No watchers are registered for project '${projectName}'`);
}
return;
2016-08-16 23:21:09 +02:00
}
clearMap(watchers, closeFileWatcher);
2016-12-05 23:13:32 +01:00
this.projectWatchers.delete(projectName);
2016-08-16 23:21:09 +02:00
if (this.log.isEnabled()) {
this.log.writeLine(`Closing file watchers for project '${projectName}' - done.`);
}
}
install(req: DiscoverTypings) {
if (this.log.isEnabled()) {
this.log.writeLine(`Got install request ${JSON.stringify(req)}`);
}
2016-08-12 20:04:43 +02:00
2016-09-26 20:33:25 +02:00
// load existing typing information from the cache
if (req.cachePath) {
if (this.log.isEnabled()) {
this.log.writeLine(`Request specifies cache path '${req.cachePath}', loading cached information...`);
}
this.processCacheLocation(req.cachePath);
}
if (this.safeList === undefined) {
2017-07-28 01:07:50 +02:00
this.initializeSafeList();
}
2016-08-12 20:04:43 +02:00
const discoverTypingsResult = JsTyping.discoverTypings(
this.installTypingHost,
this.log.isEnabled() ? (s => this.log.writeLine(s)) : undefined,
2016-08-12 20:04:43 +02:00
req.fileNames,
req.projectRootPath,
Enable '--strictNullChecks' (#22088) * Enable '--strictNullChecks' * Fix API baselines * Make sys.getEnvironmentVariable non-nullable * make properties optional instead of using `| undefined` in thier type * reportDiagnostics should be required * Declare firstAccessor as non-nullable * Make `some` a type guard * Fix `getEnvironmentVariable` definition in tests * Pretend transformFlags are always defined * Fix one more use of sys.getEnvironmentVariable * `requiredResponse` accepts undefined, remove assertions * Mark optional properties as optional instead of using `| undefined` * Mark optional properties as optional instead of using ` | undefined` * Remove unnecessary null assertions * Put the bang on the declaration instead of every use * Make `createMapFromTemplate` require a parameter * Mark `EmitResult.emittedFiles` and `EmitResult.sourceMaps` as optional * Plumb through undefined in emitLsit and EmitExpressionList * `ElementAccessExpression.argumentExpression` can not be `undefined` * Add overloads for `writeTokenText` * Make `shouldWriteSeparatingLineTerminator` argument non-nullable * Make `synthesizedNodeStartsOnNewLine` argument required * `PropertyAssignment.initializer` cannot be undefined * Use one `!` at declaration site instead of on every use site * Capture host in a constant and avoid null assertions * Remove few more unused assertions * Update baselines * Use parameter defaults * Update baselines * Fix lint * Make Symbol#valueDeclaration and Symbol#declarations non-optional to reduce assertions * Make Node#symbol and Type#symbol non-optional to reduce assertions * Make `flags` non-nullable to reduce assertions * Convert some asserts to type guards * Make `isNonLocalAlias` a type guard * Add overload for `getSymbolOfNode` for `Declaration` * Some more `getSymbolOfNode` changes * Push undefined suppression into `typeToTypeNodeHelper` * `NodeBuilderContext.tracker` is never `undefined` * use `Debug.assertDefined` * Remove unnecessary tag * Mark `LiteralType.freshType` and `LiteralTupe.regularType` as required
2018-05-22 23:46:57 +02:00
this.safeList!,
this.packageNameToTypingLocation,
req.typeAcquisition,
req.unresolvedImports,
this.typesRegistry);
2016-08-17 23:47:54 +02:00
if (this.log.isEnabled()) {
this.log.writeLine(`Finished typings discovery: ${JSON.stringify(discoverTypingsResult)}`);
}
2016-08-12 20:04:43 +02:00
2016-08-12 21:14:25 +02:00
// start watching files
this.watchFiles(req.projectName, discoverTypingsResult.filesToWatch, req.projectRootPath, req.watchOptions);
// install typings
if (discoverTypingsResult.newTypingNames.length) {
this.installTypings(req, req.cachePath || this.globalCachePath, discoverTypingsResult.cachedTypingPaths, discoverTypingsResult.newTypingNames);
}
else {
this.sendResponse(this.createSetTypings(req, discoverTypingsResult.cachedTypingPaths));
if (this.log.isEnabled()) {
this.log.writeLine(`No new typings were requested as a result of typings discovery`);
}
}
2016-08-12 20:04:43 +02:00
}
2017-07-28 01:07:50 +02:00
private initializeSafeList() {
// Prefer the safe list from the types map if it exists
if (this.typesMapLocation) {
const safeListFromMap = JsTyping.loadTypesMap(this.installTypingHost, this.typesMapLocation);
if (safeListFromMap) {
this.log.writeLine(`Loaded safelist from types map file '${this.typesMapLocation}'`);
this.safeList = safeListFromMap;
return;
}
this.log.writeLine(`Failed to load safelist from types map file '${this.typesMapLocation}'`);
}
this.safeList = JsTyping.loadSafeList(this.installTypingHost, this.safeListPath);
}
private processCacheLocation(cacheLocation: string) {
if (this.log.isEnabled()) {
this.log.writeLine(`Processing cache location '${cacheLocation}'`);
}
if (this.knownCachesSet.has(cacheLocation)) {
if (this.log.isEnabled()) {
2016-08-17 23:47:54 +02:00
this.log.writeLine(`Cache location was already processed...`);
}
return;
}
2016-08-26 01:25:34 +02:00
const packageJson = combinePaths(cacheLocation, "package.json");
const packageLockJson = combinePaths(cacheLocation, "package-lock.json");
if (this.log.isEnabled()) {
2016-08-26 01:25:34 +02:00
this.log.writeLine(`Trying to find '${packageJson}'...`);
}
if (this.installTypingHost.fileExists(packageJson) && this.installTypingHost.fileExists(packageLockJson)) {
const npmConfig = JSON.parse(this.installTypingHost.readFile(packageJson)!) as NpmConfig; // TODO: GH#18217
const npmLock = JSON.parse(this.installTypingHost.readFile(packageLockJson)!) as NpmLock; // TODO: GH#18217
if (this.log.isEnabled()) {
this.log.writeLine(`Loaded content of '${packageJson}': ${JSON.stringify(npmConfig)}`);
this.log.writeLine(`Loaded content of '${packageLockJson}'`);
}
if (npmConfig.devDependencies && npmLock.dependencies) {
2016-08-26 01:25:34 +02:00
for (const key in npmConfig.devDependencies) {
if (!hasProperty(npmLock.dependencies, key)) {
// if package in package.json but not package-lock.json, skip adding to cache so it is reinstalled on next use
continue;
}
2016-08-26 01:25:34 +02:00
// key is @types/<package name>
2016-08-27 01:37:31 +02:00
const packageName = getBaseFileName(key);
if (!packageName) {
continue;
}
const typingFile = typingToFileName(cacheLocation, packageName, this.installTypingHost, this.log);
2016-08-26 01:25:34 +02:00
if (!typingFile) {
this.missingTypingsSet.add(packageName);
2016-08-26 01:25:34 +02:00
continue;
}
2016-12-05 23:13:32 +01:00
const existingTypingFile = this.packageNameToTypingLocation.get(packageName);
if (existingTypingFile) {
2017-12-29 23:21:55 +01:00
if (existingTypingFile.typingLocation === typingFile) {
continue;
}
if (this.log.isEnabled()) {
this.log.writeLine(`New typing for package ${packageName} from '${typingFile}' conflicts with existing typing file '${existingTypingFile}'`);
}
}
if (this.log.isEnabled()) {
this.log.writeLine(`Adding entry into typings cache: '${packageName}' => '${typingFile}'`);
}
const info = getProperty(npmLock.dependencies, key);
const version = info && info.version;
if (!version) {
continue;
}
const newTyping: JsTyping.CachedTyping = { typingLocation: typingFile, version: new Version(version) };
2017-11-21 01:43:02 +01:00
this.packageNameToTypingLocation.set(packageName, newTyping);
}
}
}
if (this.log.isEnabled()) {
this.log.writeLine(`Finished processing cache location '${cacheLocation}'`);
}
this.knownCachesSet.add(cacheLocation);
}
private filterTypings(typingsToInstall: readonly string[]): readonly string[] {
return mapDefined(typingsToInstall, typing => {
const typingKey = mangleScopedPackageName(typing);
if (this.missingTypingsSet.has(typingKey)) {
if (this.log.isEnabled()) this.log.writeLine(`'${typing}':: '${typingKey}' is in missingTypingsSet - skipping...`);
return undefined;
}
2017-12-04 22:36:01 +01:00
const validationResult = JsTyping.validatePackageName(typing);
if (validationResult !== JsTyping.NameValidationResult.Ok) {
// add typing name to missing set so we won't process it again
this.missingTypingsSet.add(typingKey);
2017-12-04 22:36:01 +01:00
if (this.log.isEnabled()) this.log.writeLine(JsTyping.renderPackageNameValidationFailure(validationResult, typing));
return undefined;
}
if (!this.typesRegistry.has(typingKey)) {
if (this.log.isEnabled()) this.log.writeLine(`'${typing}':: Entry for package '${typingKey}' does not exist in local types registry - skipping...`);
return undefined;
2017-12-04 22:36:01 +01:00
}
if (this.packageNameToTypingLocation.get(typingKey) && JsTyping.isTypingUpToDate(this.packageNameToTypingLocation.get(typingKey)!, this.typesRegistry.get(typingKey)!)) {
if (this.log.isEnabled()) this.log.writeLine(`'${typing}':: '${typingKey}' already has an up-to-date typing - skipping...`);
return undefined;
}
return typingKey;
2017-12-04 22:36:01 +01:00
});
}
protected ensurePackageDirectoryExists(directory: string) {
const npmConfigPath = combinePaths(directory, "package.json");
if (this.log.isEnabled()) {
this.log.writeLine(`Npm config file: ${npmConfigPath}`);
}
if (!this.installTypingHost.fileExists(npmConfigPath)) {
if (this.log.isEnabled()) {
this.log.writeLine(`Npm config file: '${npmConfigPath}' is missing, creating new one...`);
}
this.ensureDirectoryExists(directory, this.installTypingHost);
this.installTypingHost.writeFile(npmConfigPath, '{ "private": true }');
2016-08-12 21:14:25 +02:00
}
}
2016-08-12 21:14:25 +02:00
private installTypings(req: DiscoverTypings, cachePath: string, currentlyCachedTypings: string[], typingsToInstall: string[]) {
if (this.log.isEnabled()) {
this.log.writeLine(`Installing typings ${JSON.stringify(typingsToInstall)}`);
}
const filteredTypings = this.filterTypings(typingsToInstall);
if (filteredTypings.length === 0) {
if (this.log.isEnabled()) {
this.log.writeLine(`All typings are known to be missing or invalid - no need to install more typings`);
}
this.sendResponse(this.createSetTypings(req, currentlyCachedTypings));
return;
2016-08-12 20:04:43 +02:00
}
2016-08-12 21:14:25 +02:00
this.ensurePackageDirectoryExists(cachePath);
const requestId = this.installRunCount;
this.installRunCount++;
// send progress event
this.sendResponse({
kind: EventBeginInstallTypes,
eventId: requestId,
// qualified explicitly to prevent occasional shadowing
// eslint-disable-next-line @typescript-eslint/no-unnecessary-qualifier
typingsInstallerVersion: ts.version,
projectName: req.projectName
} as BeginInstallTypes);
const scopedTypings = filteredTypings.map(typingsName);
this.installTypingsAsync(requestId, scopedTypings, cachePath, ok => {
try {
if (!ok) {
if (this.log.isEnabled()) {
this.log.writeLine(`install request failed, marking packages as missing to prevent repeated requests: ${JSON.stringify(filteredTypings)}`);
}
for (const typing of filteredTypings) {
this.missingTypingsSet.add(typing);
}
return;
}
// TODO: watch project directory
if (this.log.isEnabled()) {
this.log.writeLine(`Installed typings ${JSON.stringify(scopedTypings)}`);
}
const installedTypingFiles: string[] = [];
for (const packageName of filteredTypings) {
const typingFile = typingToFileName(cachePath, packageName, this.installTypingHost, this.log);
if (!typingFile) {
this.missingTypingsSet.add(packageName);
continue;
}
2018-01-19 22:13:51 +01:00
// packageName is guaranteed to exist in typesRegistry by filterTypings
Enable '--strictNullChecks' (#22088) * Enable '--strictNullChecks' * Fix API baselines * Make sys.getEnvironmentVariable non-nullable * make properties optional instead of using `| undefined` in thier type * reportDiagnostics should be required * Declare firstAccessor as non-nullable * Make `some` a type guard * Fix `getEnvironmentVariable` definition in tests * Pretend transformFlags are always defined * Fix one more use of sys.getEnvironmentVariable * `requiredResponse` accepts undefined, remove assertions * Mark optional properties as optional instead of using `| undefined` * Mark optional properties as optional instead of using ` | undefined` * Remove unnecessary null assertions * Put the bang on the declaration instead of every use * Make `createMapFromTemplate` require a parameter * Mark `EmitResult.emittedFiles` and `EmitResult.sourceMaps` as optional * Plumb through undefined in emitLsit and EmitExpressionList * `ElementAccessExpression.argumentExpression` can not be `undefined` * Add overloads for `writeTokenText` * Make `shouldWriteSeparatingLineTerminator` argument non-nullable * Make `synthesizedNodeStartsOnNewLine` argument required * `PropertyAssignment.initializer` cannot be undefined * Use one `!` at declaration site instead of on every use site * Capture host in a constant and avoid null assertions * Remove few more unused assertions * Update baselines * Use parameter defaults * Update baselines * Fix lint * Make Symbol#valueDeclaration and Symbol#declarations non-optional to reduce assertions * Make Node#symbol and Type#symbol non-optional to reduce assertions * Make `flags` non-nullable to reduce assertions * Convert some asserts to type guards * Make `isNonLocalAlias` a type guard * Add overload for `getSymbolOfNode` for `Declaration` * Some more `getSymbolOfNode` changes * Push undefined suppression into `typeToTypeNodeHelper` * `NodeBuilderContext.tracker` is never `undefined` * use `Debug.assertDefined` * Remove unnecessary tag * Mark `LiteralType.freshType` and `LiteralTupe.regularType` as required
2018-05-22 23:46:57 +02:00
const distTags = this.typesRegistry.get(packageName)!;
const newVersion = new Version(distTags[`ts${versionMajorMinor}`] || distTags[this.latestDistTag]);
const newTyping: JsTyping.CachedTyping = { typingLocation: typingFile, version: newVersion };
this.packageNameToTypingLocation.set(packageName, newTyping);
installedTypingFiles.push(typingFile);
2016-08-26 01:25:34 +02:00
}
if (this.log.isEnabled()) {
this.log.writeLine(`Installed typing files ${JSON.stringify(installedTypingFiles)}`);
2016-09-01 06:14:24 +02:00
}
this.sendResponse(this.createSetTypings(req, currentlyCachedTypings.concat(installedTypingFiles)));
}
finally {
const response: EndInstallTypes = {
kind: EventEndInstallTypes,
eventId: requestId,
projectName: req.projectName,
packagesToInstall: scopedTypings,
installSuccess: ok,
// qualified explicitly to prevent occasional shadowing
// eslint-disable-next-line @typescript-eslint/no-unnecessary-qualifier
typingsInstallerVersion: ts.version
};
this.sendResponse(response);
}
2016-08-12 21:14:25 +02:00
});
2016-08-12 20:04:43 +02:00
}
2016-08-13 08:04:17 +02:00
private ensureDirectoryExists(directory: string, host: InstallTypingHost): void {
const directoryName = getDirectoryPath(directory);
if (!host.directoryExists(directoryName)) {
this.ensureDirectoryExists(directoryName, host);
}
if (!host.directoryExists(directory)) {
host.createDirectory(directory);
}
}
private watchFiles(projectName: string, files: string[], projectRootPath: Path, options: WatchOptions | undefined) {
2016-08-16 23:21:09 +02:00
if (!files.length) {
// shut down existing watchers
this.closeWatchers(projectName);
2016-08-16 23:21:09 +02:00
return;
}
Enable '--strictNullChecks' (#22088) * Enable '--strictNullChecks' * Fix API baselines * Make sys.getEnvironmentVariable non-nullable * make properties optional instead of using `| undefined` in thier type * reportDiagnostics should be required * Declare firstAccessor as non-nullable * Make `some` a type guard * Fix `getEnvironmentVariable` definition in tests * Pretend transformFlags are always defined * Fix one more use of sys.getEnvironmentVariable * `requiredResponse` accepts undefined, remove assertions * Mark optional properties as optional instead of using `| undefined` * Mark optional properties as optional instead of using ` | undefined` * Remove unnecessary null assertions * Put the bang on the declaration instead of every use * Make `createMapFromTemplate` require a parameter * Mark `EmitResult.emittedFiles` and `EmitResult.sourceMaps` as optional * Plumb through undefined in emitLsit and EmitExpressionList * `ElementAccessExpression.argumentExpression` can not be `undefined` * Add overloads for `writeTokenText` * Make `shouldWriteSeparatingLineTerminator` argument non-nullable * Make `synthesizedNodeStartsOnNewLine` argument required * `PropertyAssignment.initializer` cannot be undefined * Use one `!` at declaration site instead of on every use site * Capture host in a constant and avoid null assertions * Remove few more unused assertions * Update baselines * Use parameter defaults * Update baselines * Fix lint * Make Symbol#valueDeclaration and Symbol#declarations non-optional to reduce assertions * Make Node#symbol and Type#symbol non-optional to reduce assertions * Make `flags` non-nullable to reduce assertions * Convert some asserts to type guards * Make `isNonLocalAlias` a type guard * Add overload for `getSymbolOfNode` for `Declaration` * Some more `getSymbolOfNode` changes * Push undefined suppression into `typeToTypeNodeHelper` * `NodeBuilderContext.tracker` is never `undefined` * use `Debug.assertDefined` * Remove unnecessary tag * Mark `LiteralType.freshType` and `LiteralTupe.regularType` as required
2018-05-22 23:46:57 +02:00
let watchers = this.projectWatchers.get(projectName)!;
2020-06-26 01:03:25 +02:00
const toRemove = new Map<string, FileWatcher>();
if (!watchers) {
2020-06-26 01:03:25 +02:00
watchers = new Map();
this.projectWatchers.set(projectName, watchers);
}
else {
copyEntries(watchers, toRemove);
}
// handler should be invoked once for the entire set of files since it will trigger full rediscovery of typings
watchers.isInvoked = false;
const isLoggingEnabled = this.log.isEnabled();
const createProjectWatcher = (path: string, projectWatcherType: ProjectWatcherType) => {
const canonicalPath = this.toCanonicalFileName(path);
toRemove.delete(canonicalPath);
if (watchers.has(canonicalPath)) {
return;
}
if (isLoggingEnabled) {
this.log.writeLine(`${projectWatcherType}:: Added:: WatchInfo: ${path}`);
}
const watcher = projectWatcherType === ProjectWatcherType.FileWatcher ?
this.watchFactory.watchFile(path, () => {
if (!watchers.isInvoked) {
watchers.isInvoked = true;
this.sendResponse({ projectName, kind: ActionInvalidate });
}
}, PollingInterval.High, options, projectName, watchers) :
this.watchFactory.watchDirectory(path, f => {
if (watchers.isInvoked || !fileExtensionIs(f, Extension.Json)) {
return;
}
if (isPackageOrBowerJson(f, this.installTypingHost.useCaseSensitiveFileNames) &&
!sameFiles(f, this.globalCachePackageJsonPath, this.installTypingHost.useCaseSensitiveFileNames)) {
watchers.isInvoked = true;
this.sendResponse({ projectName, kind: ActionInvalidate });
}
}, WatchDirectoryFlags.Recursive, options, projectName, watchers);
watchers.set(canonicalPath, isLoggingEnabled ? {
close: () => {
this.log.writeLine(`${projectWatcherType}:: Closed:: WatchInfo: ${path}`);
watcher.close();
}
} : watcher);
};
// Create watches from list of files
for (const file of files) {
if (file.endsWith("/package.json") || file.endsWith("/bower.json")) {
// package.json or bower.json exists, watch the file to detect changes and update typings
createProjectWatcher(file, ProjectWatcherType.FileWatcher);
continue;
}
// path in projectRoot, watch project root
if (containsPath(projectRootPath, file, projectRootPath, !this.installTypingHost.useCaseSensitiveFileNames)) {
const subDirectory = file.indexOf(directorySeparator, projectRootPath.length + 1);
if (subDirectory !== -1) {
// Watch subDirectory
createProjectWatcher(file.substr(0, subDirectory), ProjectWatcherType.DirectoryWatcher);
}
else {
// Watch the directory itself
createProjectWatcher(file, ProjectWatcherType.DirectoryWatcher);
}
continue;
}
// path in global cache, watch global cache
if (containsPath(this.globalCachePath, file, projectRootPath, !this.installTypingHost.useCaseSensitiveFileNames)) {
createProjectWatcher(this.globalCachePath, ProjectWatcherType.DirectoryWatcher);
continue;
}
// watch node_modules or bower_components
createProjectWatcher(file, ProjectWatcherType.DirectoryWatcher);
}
// Remove unused watches
toRemove.forEach((watch, path) => {
watch.close();
watchers.delete(path);
});
2016-08-12 20:04:43 +02:00
}
2016-08-16 23:21:09 +02:00
private createSetTypings(request: DiscoverTypings, typings: string[]): SetTypings {
2016-08-12 20:04:43 +02:00
return {
projectName: request.projectName,
typeAcquisition: request.typeAcquisition,
2016-08-12 20:04:43 +02:00
compilerOptions: request.compilerOptions,
2016-08-16 23:21:09 +02:00
typings,
unresolvedImports: request.unresolvedImports,
Merge release-2.1 into master (#12157) * Update LKG * Update version * Update LKG * Skip overloads with too-short function parameters If the parameter of an overload is a function and the argument is also a function, skip the overload if the parameter has fewer arguments than the argument does. That overload cannot possibly apply, and should not participate in, for example, contextual typing. Example: ```ts interface I { (a: number): void; (b: string, c): void; } declare function f(i: I): void; f((x, y) => {}); ``` This code now skips the first overload instead of considering. This was a longstanding bug but was only uncovered now that more functions expressions are context sensitive. * Test skip overloads w/too-short function params 1. Update changed baseline. 2. Add a new test with baseline. * Minor style improvements * Ignore optionality when skipping overloads * Do not use contextual signatures with too few parameters * isAritySmaller runs later: getNonGenericSignature * rewrite void-returning statements in constructors that capture result of super call (#11868) * rewrite void-returning statements in constructors that capture result of super call * linter * Update LKG * Fix emit inferred type which is a generic type-alias both fully and partially fill type parameters * Add tests and baselines * Skip trying to use alias if there is target type * Update baselines * Add diagnostics to remind adding tsconfig file for certain external project (#11932) * Add diagnostics for certain external project * Show tsconfig suggestion * fix lint error * Address pr * fix comment * Update error message * Flag for not overwrite js files by default without generating errors (#11980) * WIP * Properly naming things * refactor * apply the option to all files and check out options * Fix typo * Update LKG * lockLinter * use local registry to check if typings package exist (#12014) (#12032) use local registry to check if typings package exist * Add test for https://github.com/Microsoft/TypeScript/pull/11980 (#12027) * add test for the fix for overwrite emitting error * cr feedback * enable sending telemetry events to tsserver client (#12034) (#12051) enable sending telemetry events * Update LKG * Reuse subtree transform flags for incrementally parsed nodes (#12088) * Update LKG * Update version * Update LKG * Do not emit "use strict" when targeting es6 or higher or module kind is es2015 and the file is external module * Add tests and baselines * [Release 2.1] fix11754 global augmentation (#12133) * Exclude global augmentation from module resolution logic * Address PR: check using string literal instead of NodeFlags.globalAugmentation * Remove comment
2016-11-10 23:28:34 +01:00
kind: ActionSet
2016-08-12 20:04:43 +02:00
};
}
private installTypingsAsync(requestId: number, packageNames: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void {
this.pendingRunRequests.unshift({ requestId, packageNames, cwd, onRequestCompleted });
2016-09-20 23:14:51 +02:00
this.executeWithThrottling();
}
private executeWithThrottling() {
while (this.inFlightRequestCount < this.throttleLimit && this.pendingRunRequests.length) {
this.inFlightRequestCount++;
Enable '--strictNullChecks' (#22088) * Enable '--strictNullChecks' * Fix API baselines * Make sys.getEnvironmentVariable non-nullable * make properties optional instead of using `| undefined` in thier type * reportDiagnostics should be required * Declare firstAccessor as non-nullable * Make `some` a type guard * Fix `getEnvironmentVariable` definition in tests * Pretend transformFlags are always defined * Fix one more use of sys.getEnvironmentVariable * `requiredResponse` accepts undefined, remove assertions * Mark optional properties as optional instead of using `| undefined` * Mark optional properties as optional instead of using ` | undefined` * Remove unnecessary null assertions * Put the bang on the declaration instead of every use * Make `createMapFromTemplate` require a parameter * Mark `EmitResult.emittedFiles` and `EmitResult.sourceMaps` as optional * Plumb through undefined in emitLsit and EmitExpressionList * `ElementAccessExpression.argumentExpression` can not be `undefined` * Add overloads for `writeTokenText` * Make `shouldWriteSeparatingLineTerminator` argument non-nullable * Make `synthesizedNodeStartsOnNewLine` argument required * `PropertyAssignment.initializer` cannot be undefined * Use one `!` at declaration site instead of on every use site * Capture host in a constant and avoid null assertions * Remove few more unused assertions * Update baselines * Use parameter defaults * Update baselines * Fix lint * Make Symbol#valueDeclaration and Symbol#declarations non-optional to reduce assertions * Make Node#symbol and Type#symbol non-optional to reduce assertions * Make `flags` non-nullable to reduce assertions * Convert some asserts to type guards * Make `isNonLocalAlias` a type guard * Add overload for `getSymbolOfNode` for `Declaration` * Some more `getSymbolOfNode` changes * Push undefined suppression into `typeToTypeNodeHelper` * `NodeBuilderContext.tracker` is never `undefined` * use `Debug.assertDefined` * Remove unnecessary tag * Mark `LiteralType.freshType` and `LiteralTupe.regularType` as required
2018-05-22 23:46:57 +02:00
const request = this.pendingRunRequests.pop()!;
this.installWorker(request.requestId, request.packageNames, request.cwd, ok => {
2016-09-20 23:14:51 +02:00
this.inFlightRequestCount--;
request.onRequestCompleted(ok);
2016-09-20 23:14:51 +02:00
this.executeWithThrottling();
});
}
}
protected abstract installWorker(requestId: number, packageNames: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void;
protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes): void;
2018-06-02 01:03:04 +02:00
protected readonly latestDistTag = "latest";
2016-08-12 20:04:43 +02:00
}
/* @internal */
export function typingsName(packageName: string): string {
return `@types/${packageName}@ts${versionMajorMinor}`;
}
}