Path: blob/main/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as platform from '../../../../base/common/platform.js';6import { IExtensionDescription, IExtension } from '../../../../platform/extensions/common/extensions.js';7import { dedupExtensions } from '../common/extensionsUtil.js';8import { IExtensionsScannerService, IScannedExtension, toExtensionDescription as toExtensionDescriptionFromScannedExtension } from '../../../../platform/extensionManagement/common/extensionsScannerService.js';9import { ILogService } from '../../../../platform/log/common/log.js';10import Severity from '../../../../base/common/severity.js';11import { localize } from '../../../../nls.js';12import { INotificationService } from '../../../../platform/notification/common/notification.js';13import { IHostService } from '../../host/browser/host.js';14import { timeout } from '../../../../base/common/async.js';15import { IUserDataProfileService } from '../../userDataProfile/common/userDataProfile.js';16import { getErrorMessage } from '../../../../base/common/errors.js';17import { IWorkbenchExtensionManagementService } from '../../extensionManagement/common/extensionManagement.js';18import { toExtensionDescription } from '../common/extensions.js';19import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';2021export class CachedExtensionScanner {2223public readonly scannedExtensions: Promise<IExtensionDescription[]>;24private _scannedExtensionsResolve!: (result: IExtensionDescription[]) => void;25private _scannedExtensionsReject!: (err: any) => void;2627constructor(28@INotificationService private readonly _notificationService: INotificationService,29@IHostService private readonly _hostService: IHostService,30@IExtensionsScannerService private readonly _extensionsScannerService: IExtensionsScannerService,31@IUserDataProfileService private readonly _userDataProfileService: IUserDataProfileService,32@IWorkbenchExtensionManagementService private readonly _extensionManagementService: IWorkbenchExtensionManagementService,33@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,34@ILogService private readonly _logService: ILogService,35) {36this.scannedExtensions = new Promise<IExtensionDescription[]>((resolve, reject) => {37this._scannedExtensionsResolve = resolve;38this._scannedExtensionsReject = reject;39});40}4142public async startScanningExtensions(): Promise<void> {43try {44const extensions = await this._scanInstalledExtensions();45this._scannedExtensionsResolve(extensions);46} catch (err) {47this._scannedExtensionsReject(err);48}49}5051private async _scanInstalledExtensions(): Promise<IExtensionDescription[]> {52try {53const language = platform.language;54const result = await Promise.allSettled([55this._extensionsScannerService.scanSystemExtensions({ language, checkControlFile: true }),56this._extensionsScannerService.scanUserExtensions({ language, profileLocation: this._userDataProfileService.currentProfile.extensionsResource, useCache: true }),57this._environmentService.remoteAuthority ? [] : this._extensionManagementService.getInstalledWorkspaceExtensions(false)58]);5960let hasErrors = false;6162let scannedSystemExtensions: IScannedExtension[] = [];63if (result[0].status === 'fulfilled') {64scannedSystemExtensions = result[0].value;65} else {66hasErrors = true;67this._logService.error(`Error scanning system extensions:`, getErrorMessage(result[0].reason));68}6970let scannedUserExtensions: IScannedExtension[] = [];71if (result[1].status === 'fulfilled') {72scannedUserExtensions = result[1].value;73} else {74hasErrors = true;75this._logService.error(`Error scanning user extensions:`, getErrorMessage(result[1].reason));76}7778let workspaceExtensions: IExtension[] = [];79if (result[2].status === 'fulfilled') {80workspaceExtensions = result[2].value;81} else {82hasErrors = true;83this._logService.error(`Error scanning workspace extensions:`, getErrorMessage(result[2].reason));84}8586const scannedDevelopedExtensions: IScannedExtension[] = [];87try {88const allScannedDevelopedExtensions = await this._extensionsScannerService.scanExtensionsUnderDevelopment([...scannedSystemExtensions, ...scannedUserExtensions], { language, includeInvalid: true });89const invalidExtensions: IScannedExtension[] = [];90for (const extensionUnderDevelopment of allScannedDevelopedExtensions) {91if (extensionUnderDevelopment.isValid) {92scannedDevelopedExtensions.push(extensionUnderDevelopment);93} else {94invalidExtensions.push(extensionUnderDevelopment);95}96}97if (invalidExtensions.length > 0) {98this._notificationService.prompt(99Severity.Warning,100invalidExtensions.length === 1101? localize('extensionUnderDevelopment.invalid', "Failed loading extension '{0}' under development because it is invalid: {1}", invalidExtensions[0].location.fsPath, invalidExtensions[0].validations[0][1])102: localize('extensionsUnderDevelopment.invalid', "Failed loading extensions {0} under development because they are invalid: {1}", invalidExtensions.map(ext => `'${ext.location.fsPath}'`).join(', '), invalidExtensions.map(ext => `${ext.validations[0][1]}`).join(', ')),103[]104);105}106} catch (error) {107this._logService.error(error);108}109110const system = scannedSystemExtensions.map(e => toExtensionDescriptionFromScannedExtension(e, false));111const user = scannedUserExtensions.map(e => toExtensionDescriptionFromScannedExtension(e, false));112const workspace = workspaceExtensions.map(e => toExtensionDescription(e, false));113const development = scannedDevelopedExtensions.map(e => toExtensionDescriptionFromScannedExtension(e, true));114const r = dedupExtensions(system, user, workspace, development, this._logService);115116if (!hasErrors) {117const disposable = this._extensionsScannerService.onDidChangeCache(() => {118disposable.dispose();119this._notificationService.prompt(120Severity.Error,121localize('extensionCache.invalid', "Extensions have been modified on disk. Please reload the window."),122[{123label: localize('reloadWindow', "Reload Window"),124run: () => this._hostService.reload()125}]126);127});128timeout(5000).then(() => disposable.dispose());129}130131return r;132} catch (err) {133this._logService.error(`Error scanning installed extensions:`);134this._logService.error(err);135return [];136}137}138139}140141142