@codingame/monaco-vscode-extension-gallery-service-override 4.4.0 → 4.5.0-improve-code-splitting.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/extensionGallery.js +21 -16
- package/package.json +10 -3
- package/vscode/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.js +7 -6
- package/vscode/src/vs/platform/extensionManagement/common/extensionGalleryService.js +12 -11
- package/vscode/src/vs/platform/extensionManagement/common/extensionManagementIpc.js +1 -1
- package/vscode/src/vs/platform/extensionManagement/common/extensionTipsService.js +2 -3
- package/vscode/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.js +334 -0
- package/vscode/src/vs/platform/extensionManagement/common/extensionsScannerService.js +904 -0
- package/vscode/src/vs/platform/extensionRecommendations/common/extensionRecommendations.js +9 -0
- package/vscode/src/vs/platform/userDataProfile/common/userDataProfileIpc.js +67 -0
- package/vscode/src/vs/platform/userDataSync/common/ignoredExtensions.js +65 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.js +18 -14
- package/vscode/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.js +2 -2
- package/vscode/src/vs/workbench/contrib/extensions/browser/deprecatedExtensionsChecker.js +8 -6
- package/vscode/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.js +1 -1
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionEditor.js +25 -21
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant.js +5 -5
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionFeaturesTab.js +7 -6
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.js +18 -16
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.js +9 -9
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensions.contribution.js +31 -24
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActivationProgress.js +3 -3
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider.js +1 -1
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsDependencyChecker.js +7 -5
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsList.js +8 -6
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsQuickAccess.js +4 -4
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer.js +6 -5
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.js +29 -24
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews.js +34 -30
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.js +14 -13
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.js +33 -28
- package/vscode/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.js +6 -6
- package/vscode/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.js +1 -1
- package/vscode/src/vs/workbench/contrib/extensions/browser/languageRecommendations.js +1 -1
- package/vscode/src/vs/workbench/contrib/extensions/browser/remoteRecommendations.js +1 -1
- package/vscode/src/vs/workbench/contrib/extensions/browser/unsupportedExtensionsMigrationContribution.js +4 -4
- package/vscode/src/vs/workbench/contrib/extensions/browser/webRecommendations.js +2 -2
- package/vscode/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.js +6 -6
- package/vscode/src/vs/workbench/contrib/extensions/common/reportExtensionIssueAction.js +1 -1
- package/vscode/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.js +19 -16
- package/vscode/src/vs/workbench/services/extensionManagement/browser/extensionsProfileScannerService.js +8 -7
- package/vscode/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.js +17 -17
- package/vscode/src/vs/workbench/services/extensionManagement/common/extensionFeaturesManagemetService.js +4 -4
- package/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.js +2 -3
- package/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagementService.js +21 -20
- package/vscode/src/vs/workbench/services/extensionManagement/common/remoteExtensionManagementService.js +4 -4
- package/vscode/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.js +9 -9
- package/vscode/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.js +2 -2
- package/vscode/src/vs/workbench/services/extensions/browser/extensionUrlHandler.js +339 -0
- package/vscode/src/vs/workbench/services/extensions/browser/extensionsScannerService.js +9 -9
- package/vscode/src/vs/workbench/services/userDataProfile/common/remoteUserDataProfiles.js +151 -0
- package/override/vs/platform/dialogs/common/dialogs.js +0 -10
|
@@ -0,0 +1,904 @@
|
|
|
1
|
+
import { __decorate, __param } from 'vscode/external/tslib/tslib.es6.js';
|
|
2
|
+
import { coalesce } from 'vscode/vscode/vs/base/common/arrays';
|
|
3
|
+
import { ThrottledDelayer } from 'vscode/vscode/vs/base/common/async';
|
|
4
|
+
import { equals } from 'vscode/vscode/vs/base/common/objects';
|
|
5
|
+
import { VSBuffer } from 'vscode/vscode/vs/base/common/buffer';
|
|
6
|
+
import { getErrorMessage } from 'vscode/vscode/vs/base/common/errors';
|
|
7
|
+
import { parse, getNodeType } from 'vscode/vscode/vs/base/common/json';
|
|
8
|
+
import { getParseErrorMessage } from 'vscode/vscode/vs/base/common/jsonErrorMessages';
|
|
9
|
+
import { Disposable } from 'vscode/vscode/vs/base/common/lifecycle';
|
|
10
|
+
import { Schemas, FileAccess } from 'vscode/vscode/vs/base/common/network';
|
|
11
|
+
import { basename, normalize, join } from 'vscode/vscode/vs/base/common/path';
|
|
12
|
+
import { language } from 'vscode/vscode/vs/base/common/platform';
|
|
13
|
+
import { joinPath, isEqual, basename as basename$1 } from 'vscode/vscode/vs/base/common/resources';
|
|
14
|
+
import { semverExports } from 'vscode/external/vscode-semver/semver.js';
|
|
15
|
+
import Severity$1 from 'vscode/vscode/vs/base/common/severity';
|
|
16
|
+
import { isEmptyObject } from 'vscode/vscode/vs/base/common/types';
|
|
17
|
+
import { URI } from 'vscode/vscode/vs/base/common/uri';
|
|
18
|
+
import { localizeWithPath } from 'vscode/vscode/vs/nls';
|
|
19
|
+
import { IEnvironmentService } from 'vscode/vscode/vs/platform/environment/common/environment.service';
|
|
20
|
+
import { computeTargetPlatform, areSameExtensions, ExtensionKey, getGalleryExtensionId } from 'vscode/vscode/vs/platform/extensionManagement/common/extensionManagementUtil';
|
|
21
|
+
import { ExtensionIdentifierMap, UNDEFINED_PUBLISHER, BUILTIN_MANIFEST_CACHE_FILE, USER_MANIFEST_CACHE_FILE } from 'vscode/vscode/vs/platform/extensions/common/extensions';
|
|
22
|
+
import { validateExtensionManifest } from 'vscode/vscode/vs/platform/extensions/common/extensionValidator';
|
|
23
|
+
import { toFileOperationResult } from 'vscode/vscode/vs/platform/files/common/files';
|
|
24
|
+
import { IFileService } from 'vscode/vscode/vs/platform/files/common/files.service';
|
|
25
|
+
import { IInstantiationService } from 'vscode/vscode/vs/platform/instantiation/common/instantiation';
|
|
26
|
+
import { ILogService } from 'vscode/vscode/vs/platform/log/common/log.service';
|
|
27
|
+
import { IProductService } from 'vscode/vscode/vs/platform/product/common/productService.service';
|
|
28
|
+
import { Emitter } from 'vscode/vscode/vs/base/common/event';
|
|
29
|
+
import { revive } from 'vscode/vscode/vs/base/common/marshalling';
|
|
30
|
+
import { ExtensionsProfileScanningError } from './extensionsProfileScannerService.js';
|
|
31
|
+
import { IExtensionsProfileScannerService } from 'vscode/vscode/vs/platform/extensionManagement/common/extensionsProfileScannerService.service';
|
|
32
|
+
import { IUserDataProfilesService } from 'vscode/vscode/vs/platform/userDataProfile/common/userDataProfile.service';
|
|
33
|
+
import { IUriIdentityService } from 'vscode/vscode/vs/platform/uriIdentity/common/uriIdentity.service';
|
|
34
|
+
import { localizeManifest } from 'vscode/vscode/vs/platform/extensionManagement/common/extensionNls';
|
|
35
|
+
|
|
36
|
+
var Translations;
|
|
37
|
+
( (function(Translations) {
|
|
38
|
+
function equals(a, b) {
|
|
39
|
+
if (a === b) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
const aKeys = ( Object.keys(a));
|
|
43
|
+
const bKeys = ( new Set());
|
|
44
|
+
for (const key of ( Object.keys(b))) {
|
|
45
|
+
bKeys.add(key);
|
|
46
|
+
}
|
|
47
|
+
if (aKeys.length !== bKeys.size) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
for (const key of aKeys) {
|
|
51
|
+
if (a[key] !== b[key]) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
bKeys.delete(key);
|
|
55
|
+
}
|
|
56
|
+
return bKeys.size === 0;
|
|
57
|
+
}
|
|
58
|
+
Translations.equals = equals;
|
|
59
|
+
})(Translations || (Translations = {})));
|
|
60
|
+
let AbstractExtensionsScannerService = class AbstractExtensionsScannerService extends Disposable {
|
|
61
|
+
constructor(systemExtensionsLocation, userExtensionsLocation, extensionsControlLocation, currentProfile, userDataProfilesService, extensionsProfileScannerService, fileService, logService, environmentService, productService, uriIdentityService, instantiationService) {
|
|
62
|
+
super();
|
|
63
|
+
this.systemExtensionsLocation = systemExtensionsLocation;
|
|
64
|
+
this.userExtensionsLocation = userExtensionsLocation;
|
|
65
|
+
this.extensionsControlLocation = extensionsControlLocation;
|
|
66
|
+
this.currentProfile = currentProfile;
|
|
67
|
+
this.userDataProfilesService = userDataProfilesService;
|
|
68
|
+
this.extensionsProfileScannerService = extensionsProfileScannerService;
|
|
69
|
+
this.fileService = fileService;
|
|
70
|
+
this.logService = logService;
|
|
71
|
+
this.environmentService = environmentService;
|
|
72
|
+
this.productService = productService;
|
|
73
|
+
this.uriIdentityService = uriIdentityService;
|
|
74
|
+
this.instantiationService = instantiationService;
|
|
75
|
+
this._onDidChangeCache = this._register(( new Emitter()));
|
|
76
|
+
this.onDidChangeCache = this._onDidChangeCache.event;
|
|
77
|
+
this.obsoleteFile = joinPath(this.userExtensionsLocation, '.obsolete');
|
|
78
|
+
this.systemExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, this.currentProfile, this.obsoleteFile));
|
|
79
|
+
this.userExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, this.currentProfile, this.obsoleteFile));
|
|
80
|
+
this.extensionsScanner = this._register(this.instantiationService.createInstance(ExtensionsScanner, this.obsoleteFile));
|
|
81
|
+
this.initializeDefaultProfileExtensionsPromise = undefined;
|
|
82
|
+
this._register(this.systemExtensionsCachedScanner.onDidChangeCache(() => this._onDidChangeCache.fire(0 )));
|
|
83
|
+
this._register(this.userExtensionsCachedScanner.onDidChangeCache(() => this._onDidChangeCache.fire(1 )));
|
|
84
|
+
}
|
|
85
|
+
getTargetPlatform() {
|
|
86
|
+
if (!this._targetPlatformPromise) {
|
|
87
|
+
this._targetPlatformPromise = computeTargetPlatform(this.fileService, this.logService);
|
|
88
|
+
}
|
|
89
|
+
return this._targetPlatformPromise;
|
|
90
|
+
}
|
|
91
|
+
async scanAllExtensions(systemScanOptions, userScanOptions, includeExtensionsUnderDev) {
|
|
92
|
+
const [system, user] = await Promise.all([
|
|
93
|
+
this.scanSystemExtensions(systemScanOptions),
|
|
94
|
+
this.scanUserExtensions(userScanOptions),
|
|
95
|
+
]);
|
|
96
|
+
const development = includeExtensionsUnderDev ? await this.scanExtensionsUnderDevelopment(systemScanOptions, [...system, ...user]) : [];
|
|
97
|
+
return this.dedupExtensions(system, user, development, await this.getTargetPlatform(), true);
|
|
98
|
+
}
|
|
99
|
+
async scanSystemExtensions(scanOptions) {
|
|
100
|
+
const promises = [];
|
|
101
|
+
promises.push(this.scanDefaultSystemExtensions(!!scanOptions.useCache, scanOptions.language));
|
|
102
|
+
promises.push(this.scanDevSystemExtensions(scanOptions.language, !!scanOptions.checkControlFile));
|
|
103
|
+
const [defaultSystemExtensions, devSystemExtensions] = await Promise.all(promises);
|
|
104
|
+
return this.applyScanOptions([...defaultSystemExtensions, ...devSystemExtensions], 0 , scanOptions, false);
|
|
105
|
+
}
|
|
106
|
+
async scanUserExtensions(scanOptions) {
|
|
107
|
+
const location = scanOptions.profileLocation ?? this.userExtensionsLocation;
|
|
108
|
+
this.logService.trace('Started scanning user extensions', location);
|
|
109
|
+
const profileScanOptions = this.uriIdentityService.extUri.isEqual(scanOptions.profileLocation, this.userDataProfilesService.defaultProfile.extensionsResource) ? { bailOutWhenFileNotFound: true } : undefined;
|
|
110
|
+
const extensionsScannerInput = await this.createExtensionScannerInput(location, !!scanOptions.profileLocation, 1 , !scanOptions.includeUninstalled, scanOptions.language, true, profileScanOptions, scanOptions.productVersion ?? this.getProductVersion());
|
|
111
|
+
const extensionsScanner = scanOptions.useCache && !extensionsScannerInput.devMode && extensionsScannerInput.excludeObsolete ? this.userExtensionsCachedScanner : this.extensionsScanner;
|
|
112
|
+
let extensions;
|
|
113
|
+
try {
|
|
114
|
+
extensions = await extensionsScanner.scanExtensions(extensionsScannerInput);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (error instanceof ExtensionsProfileScanningError && error.code === "ERROR_PROFILE_NOT_FOUND" ) {
|
|
118
|
+
await this.doInitializeDefaultProfileExtensions();
|
|
119
|
+
extensions = await extensionsScanner.scanExtensions(extensionsScannerInput);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
extensions = await this.applyScanOptions(extensions, 1 , scanOptions, true);
|
|
126
|
+
this.logService.trace('Scanned user extensions:', extensions.length);
|
|
127
|
+
return extensions;
|
|
128
|
+
}
|
|
129
|
+
async scanExtensionsUnderDevelopment(scanOptions, existingExtensions) {
|
|
130
|
+
if (this.environmentService.isExtensionDevelopment && this.environmentService.extensionDevelopmentLocationURI) {
|
|
131
|
+
const extensions = (await Promise.all(( this.environmentService.extensionDevelopmentLocationURI.filter(extLoc => extLoc.scheme === Schemas.file)
|
|
132
|
+
.map(async (extensionDevelopmentLocationURI) => {
|
|
133
|
+
const input = await this.createExtensionScannerInput(extensionDevelopmentLocationURI, false, 1 , true, scanOptions.language, false , undefined, scanOptions.productVersion ?? this.getProductVersion());
|
|
134
|
+
const extensions = await this.extensionsScanner.scanOneOrMultipleExtensions(input);
|
|
135
|
+
return ( extensions.map(extension => {
|
|
136
|
+
extension.type = existingExtensions.find(e => areSameExtensions(e.identifier, extension.identifier))?.type ?? extension.type;
|
|
137
|
+
return this.extensionsScanner.validate(extension, input);
|
|
138
|
+
}));
|
|
139
|
+
}))))
|
|
140
|
+
.flat();
|
|
141
|
+
return this.applyScanOptions(extensions, 'development', scanOptions, true);
|
|
142
|
+
}
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
async scanExistingExtension(extensionLocation, extensionType, scanOptions) {
|
|
146
|
+
const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion());
|
|
147
|
+
const extension = await this.extensionsScanner.scanExtension(extensionsScannerInput);
|
|
148
|
+
if (!extension) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
if (!scanOptions.includeInvalid && !extension.isValid) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
return extension;
|
|
155
|
+
}
|
|
156
|
+
async scanOneOrMultipleExtensions(extensionLocation, extensionType, scanOptions) {
|
|
157
|
+
const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion());
|
|
158
|
+
const extensions = await this.extensionsScanner.scanOneOrMultipleExtensions(extensionsScannerInput);
|
|
159
|
+
return this.applyScanOptions(extensions, extensionType, scanOptions, true);
|
|
160
|
+
}
|
|
161
|
+
async scanMultipleExtensions(extensionLocations, extensionType, scanOptions) {
|
|
162
|
+
const extensions = [];
|
|
163
|
+
await Promise.all(( extensionLocations.map(async (extensionLocation) => {
|
|
164
|
+
const scannedExtensions = await this.scanOneOrMultipleExtensions(extensionLocation, extensionType, scanOptions);
|
|
165
|
+
extensions.push(...scannedExtensions);
|
|
166
|
+
})));
|
|
167
|
+
return this.applyScanOptions(extensions, extensionType, scanOptions, true);
|
|
168
|
+
}
|
|
169
|
+
async scanMetadata(extensionLocation) {
|
|
170
|
+
const manifestLocation = joinPath(extensionLocation, 'package.json');
|
|
171
|
+
const content = ( (await this.fileService.readFile(manifestLocation)).value.toString());
|
|
172
|
+
const manifest = JSON.parse(content);
|
|
173
|
+
return manifest.__metadata;
|
|
174
|
+
}
|
|
175
|
+
async updateMetadata(extensionLocation, metaData) {
|
|
176
|
+
const manifestLocation = joinPath(extensionLocation, 'package.json');
|
|
177
|
+
const content = ( (await this.fileService.readFile(manifestLocation)).value.toString());
|
|
178
|
+
const manifest = JSON.parse(content);
|
|
179
|
+
if (metaData.isMachineScoped === false) {
|
|
180
|
+
delete metaData.isMachineScoped;
|
|
181
|
+
}
|
|
182
|
+
if (metaData.isBuiltin === false) {
|
|
183
|
+
delete metaData.isBuiltin;
|
|
184
|
+
}
|
|
185
|
+
manifest.__metadata = { ...manifest.__metadata, ...metaData };
|
|
186
|
+
await this.fileService.writeFile(joinPath(extensionLocation, 'package.json'), VSBuffer.fromString(JSON.stringify(manifest, null, '\t')));
|
|
187
|
+
}
|
|
188
|
+
async initializeDefaultProfileExtensions() {
|
|
189
|
+
try {
|
|
190
|
+
await this.extensionsProfileScannerService.scanProfileExtensions(this.userDataProfilesService.defaultProfile.extensionsResource, { bailOutWhenFileNotFound: true });
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
if (error instanceof ExtensionsProfileScanningError && error.code === "ERROR_PROFILE_NOT_FOUND" ) {
|
|
194
|
+
await this.doInitializeDefaultProfileExtensions();
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async doInitializeDefaultProfileExtensions() {
|
|
202
|
+
if (!this.initializeDefaultProfileExtensionsPromise) {
|
|
203
|
+
this.initializeDefaultProfileExtensionsPromise = (async () => {
|
|
204
|
+
try {
|
|
205
|
+
this.logService.info('Started initializing default profile extensions in extensions installation folder.', ( this.userExtensionsLocation.toString()));
|
|
206
|
+
const userExtensions = await this.scanUserExtensions({ includeInvalid: true });
|
|
207
|
+
if (userExtensions.length) {
|
|
208
|
+
await this.extensionsProfileScannerService.addExtensionsToProfile(( userExtensions.map(e => [e, e.metadata])), this.userDataProfilesService.defaultProfile.extensionsResource);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
try {
|
|
212
|
+
await this.fileService.createFile(this.userDataProfilesService.defaultProfile.extensionsResource, VSBuffer.fromString(JSON.stringify([])));
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
if (toFileOperationResult(error) !== 1 ) {
|
|
216
|
+
this.logService.warn('Failed to create default profile extensions manifest in extensions installation folder.', ( this.userExtensionsLocation.toString()), getErrorMessage(error));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
this.logService.info('Completed initializing default profile extensions in extensions installation folder.', ( this.userExtensionsLocation.toString()));
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
this.logService.error(error);
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
this.initializeDefaultProfileExtensionsPromise = undefined;
|
|
227
|
+
}
|
|
228
|
+
})();
|
|
229
|
+
}
|
|
230
|
+
return this.initializeDefaultProfileExtensionsPromise;
|
|
231
|
+
}
|
|
232
|
+
async applyScanOptions(extensions, type, scanOptions, pickLatest) {
|
|
233
|
+
if (!scanOptions.includeAllVersions) {
|
|
234
|
+
extensions = this.dedupExtensions(type === 0 ? extensions : undefined, type === 1 ? extensions : undefined, type === 'development' ? extensions : undefined, await this.getTargetPlatform(), pickLatest);
|
|
235
|
+
}
|
|
236
|
+
if (!scanOptions.includeInvalid) {
|
|
237
|
+
extensions = extensions.filter(extension => extension.isValid);
|
|
238
|
+
}
|
|
239
|
+
return extensions.sort((a, b) => {
|
|
240
|
+
const aLastSegment = basename(a.location.fsPath);
|
|
241
|
+
const bLastSegment = basename(b.location.fsPath);
|
|
242
|
+
if (aLastSegment < bLastSegment) {
|
|
243
|
+
return -1;
|
|
244
|
+
}
|
|
245
|
+
if (aLastSegment > bLastSegment) {
|
|
246
|
+
return 1;
|
|
247
|
+
}
|
|
248
|
+
return 0;
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
dedupExtensions(system, user, development, targetPlatform, pickLatest) {
|
|
252
|
+
const pick = (existing, extension, isDevelopment) => {
|
|
253
|
+
if (existing.isValid && !extension.isValid) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
if (existing.isValid === extension.isValid) {
|
|
257
|
+
if (pickLatest && semverExports.gt(existing.manifest.version, extension.manifest.version)) {
|
|
258
|
+
this.logService.debug(`Skipping extension ${extension.location.path} with lower version ${extension.manifest.version} in favour of ${existing.location.path} with version ${existing.manifest.version}`);
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
if (semverExports.eq(existing.manifest.version, extension.manifest.version)) {
|
|
262
|
+
if (existing.type === 0 ) {
|
|
263
|
+
this.logService.debug(`Skipping extension ${extension.location.path} in favour of system extension ${existing.location.path} with same version`);
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
if (existing.targetPlatform === targetPlatform) {
|
|
267
|
+
this.logService.debug(`Skipping extension ${extension.location.path} from different target platform ${extension.targetPlatform}`);
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (isDevelopment) {
|
|
273
|
+
this.logService.warn(`Overwriting user extension ${existing.location.path} with ${extension.location.path}.`);
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
this.logService.debug(`Overwriting user extension ${existing.location.path} with ${extension.location.path}.`);
|
|
277
|
+
}
|
|
278
|
+
return true;
|
|
279
|
+
};
|
|
280
|
+
const result = ( new ExtensionIdentifierMap());
|
|
281
|
+
system?.forEach((extension) => {
|
|
282
|
+
const existing = result.get(extension.identifier.id);
|
|
283
|
+
if (!existing || pick(existing, extension, false)) {
|
|
284
|
+
result.set(extension.identifier.id, extension);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
user?.forEach((extension) => {
|
|
288
|
+
const existing = result.get(extension.identifier.id);
|
|
289
|
+
if (!existing && system && extension.type === 0 ) {
|
|
290
|
+
this.logService.debug(`Skipping obsolete system extension ${extension.location.path}.`);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (!existing || pick(existing, extension, false)) {
|
|
294
|
+
result.set(extension.identifier.id, extension);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
development?.forEach(extension => {
|
|
298
|
+
const existing = result.get(extension.identifier.id);
|
|
299
|
+
if (!existing || pick(existing, extension, true)) {
|
|
300
|
+
result.set(extension.identifier.id, extension);
|
|
301
|
+
}
|
|
302
|
+
result.set(extension.identifier.id, extension);
|
|
303
|
+
});
|
|
304
|
+
return [...( result.values())];
|
|
305
|
+
}
|
|
306
|
+
async scanDefaultSystemExtensions(useCache, language) {
|
|
307
|
+
this.logService.trace('Started scanning system extensions');
|
|
308
|
+
const extensionsScannerInput = await this.createExtensionScannerInput(this.systemExtensionsLocation, false, 0 , true, language, true, undefined, this.getProductVersion());
|
|
309
|
+
const extensionsScanner = useCache && !extensionsScannerInput.devMode ? this.systemExtensionsCachedScanner : this.extensionsScanner;
|
|
310
|
+
const result = await extensionsScanner.scanExtensions(extensionsScannerInput);
|
|
311
|
+
this.logService.trace('Scanned system extensions:', result.length);
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
314
|
+
async scanDevSystemExtensions(language, checkControlFile) {
|
|
315
|
+
const devSystemExtensionsList = this.environmentService.isBuilt ? [] : this.productService.builtInExtensions;
|
|
316
|
+
if (!devSystemExtensionsList?.length) {
|
|
317
|
+
return [];
|
|
318
|
+
}
|
|
319
|
+
this.logService.trace('Started scanning dev system extensions');
|
|
320
|
+
const builtinExtensionControl = checkControlFile ? await this.getBuiltInExtensionControl() : {};
|
|
321
|
+
const devSystemExtensionsLocations = [];
|
|
322
|
+
const devSystemExtensionsLocation = URI.file(normalize(join(( FileAccess.asFileUri('')).fsPath, '..', '.build', 'builtInExtensions')));
|
|
323
|
+
for (const extension of devSystemExtensionsList) {
|
|
324
|
+
const controlState = builtinExtensionControl[extension.name] || 'marketplace';
|
|
325
|
+
switch (controlState) {
|
|
326
|
+
case 'disabled':
|
|
327
|
+
break;
|
|
328
|
+
case 'marketplace':
|
|
329
|
+
devSystemExtensionsLocations.push(joinPath(devSystemExtensionsLocation, extension.name));
|
|
330
|
+
break;
|
|
331
|
+
default:
|
|
332
|
+
devSystemExtensionsLocations.push(URI.file(controlState));
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
const result = await Promise.all(( devSystemExtensionsLocations.map(
|
|
337
|
+
async (location) => this.extensionsScanner.scanExtension((await this.createExtensionScannerInput(location, false, 0 , true, language, true, undefined, this.getProductVersion())))
|
|
338
|
+
)));
|
|
339
|
+
this.logService.trace('Scanned dev system extensions:', result.length);
|
|
340
|
+
return coalesce(result);
|
|
341
|
+
}
|
|
342
|
+
async getBuiltInExtensionControl() {
|
|
343
|
+
try {
|
|
344
|
+
const content = await this.fileService.readFile(this.extensionsControlLocation);
|
|
345
|
+
return JSON.parse(( content.value.toString()));
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
return {};
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
async createExtensionScannerInput(location, profile, type, excludeObsolete, language$1, validate, profileScanOptions, productVersion) {
|
|
352
|
+
const translations = await this.getTranslations(language$1 ?? language);
|
|
353
|
+
const mtime = await this.getMtime(location);
|
|
354
|
+
const applicationExtensionsLocation = profile && !this.uriIdentityService.extUri.isEqual(location, this.userDataProfilesService.defaultProfile.extensionsResource) ? this.userDataProfilesService.defaultProfile.extensionsResource : undefined;
|
|
355
|
+
const applicationExtensionsLocationMtime = applicationExtensionsLocation ? await this.getMtime(applicationExtensionsLocation) : undefined;
|
|
356
|
+
return ( new ExtensionScannerInput(
|
|
357
|
+
location,
|
|
358
|
+
mtime,
|
|
359
|
+
applicationExtensionsLocation,
|
|
360
|
+
applicationExtensionsLocationMtime,
|
|
361
|
+
profile,
|
|
362
|
+
profileScanOptions,
|
|
363
|
+
type,
|
|
364
|
+
excludeObsolete,
|
|
365
|
+
validate,
|
|
366
|
+
productVersion.version,
|
|
367
|
+
productVersion.date,
|
|
368
|
+
this.productService.commit,
|
|
369
|
+
!this.environmentService.isBuilt,
|
|
370
|
+
language$1,
|
|
371
|
+
translations
|
|
372
|
+
));
|
|
373
|
+
}
|
|
374
|
+
async getMtime(location) {
|
|
375
|
+
try {
|
|
376
|
+
const stat = await this.fileService.stat(location);
|
|
377
|
+
if (typeof stat.mtime === 'number') {
|
|
378
|
+
return stat.mtime;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
catch (err) {
|
|
382
|
+
}
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
getProductVersion() {
|
|
386
|
+
return {
|
|
387
|
+
version: this.productService.version,
|
|
388
|
+
date: this.productService.date,
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
AbstractExtensionsScannerService = ( __decorate([
|
|
393
|
+
( __param(4, IUserDataProfilesService)),
|
|
394
|
+
( __param(5, IExtensionsProfileScannerService)),
|
|
395
|
+
( __param(6, IFileService)),
|
|
396
|
+
( __param(7, ILogService)),
|
|
397
|
+
( __param(8, IEnvironmentService)),
|
|
398
|
+
( __param(9, IProductService)),
|
|
399
|
+
( __param(10, IUriIdentityService)),
|
|
400
|
+
( __param(11, IInstantiationService))
|
|
401
|
+
], AbstractExtensionsScannerService));
|
|
402
|
+
class ExtensionScannerInput {
|
|
403
|
+
constructor(location, mtime, applicationExtensionslocation, applicationExtensionslocationMtime, profile, profileScanOptions, type, excludeObsolete, validate, productVersion, productDate, productCommit, devMode, language, translations) {
|
|
404
|
+
this.location = location;
|
|
405
|
+
this.mtime = mtime;
|
|
406
|
+
this.applicationExtensionslocation = applicationExtensionslocation;
|
|
407
|
+
this.applicationExtensionslocationMtime = applicationExtensionslocationMtime;
|
|
408
|
+
this.profile = profile;
|
|
409
|
+
this.profileScanOptions = profileScanOptions;
|
|
410
|
+
this.type = type;
|
|
411
|
+
this.excludeObsolete = excludeObsolete;
|
|
412
|
+
this.validate = validate;
|
|
413
|
+
this.productVersion = productVersion;
|
|
414
|
+
this.productDate = productDate;
|
|
415
|
+
this.productCommit = productCommit;
|
|
416
|
+
this.devMode = devMode;
|
|
417
|
+
this.language = language;
|
|
418
|
+
this.translations = translations;
|
|
419
|
+
}
|
|
420
|
+
static createNlsConfiguration(input) {
|
|
421
|
+
return {
|
|
422
|
+
language: input.language,
|
|
423
|
+
pseudo: input.language === 'pseudo',
|
|
424
|
+
devMode: input.devMode,
|
|
425
|
+
translations: input.translations
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
static equals(a, b) {
|
|
429
|
+
return (isEqual(a.location, b.location)
|
|
430
|
+
&& a.mtime === b.mtime
|
|
431
|
+
&& isEqual(a.applicationExtensionslocation, b.applicationExtensionslocation)
|
|
432
|
+
&& a.applicationExtensionslocationMtime === b.applicationExtensionslocationMtime
|
|
433
|
+
&& a.profile === b.profile
|
|
434
|
+
&& equals(a.profileScanOptions, b.profileScanOptions)
|
|
435
|
+
&& a.type === b.type
|
|
436
|
+
&& a.excludeObsolete === b.excludeObsolete
|
|
437
|
+
&& a.validate === b.validate
|
|
438
|
+
&& a.productVersion === b.productVersion
|
|
439
|
+
&& a.productDate === b.productDate
|
|
440
|
+
&& a.productCommit === b.productCommit
|
|
441
|
+
&& a.devMode === b.devMode
|
|
442
|
+
&& a.language === b.language
|
|
443
|
+
&& Translations.equals(a.translations, b.translations));
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
let ExtensionsScanner = class ExtensionsScanner extends Disposable {
|
|
447
|
+
constructor(obsoleteFile, extensionsProfileScannerService, uriIdentityService, fileService, logService) {
|
|
448
|
+
super();
|
|
449
|
+
this.obsoleteFile = obsoleteFile;
|
|
450
|
+
this.extensionsProfileScannerService = extensionsProfileScannerService;
|
|
451
|
+
this.uriIdentityService = uriIdentityService;
|
|
452
|
+
this.fileService = fileService;
|
|
453
|
+
this.logService = logService;
|
|
454
|
+
}
|
|
455
|
+
async scanExtensions(input) {
|
|
456
|
+
const extensions = input.profile ? await this.scanExtensionsFromProfile(input) : await this.scanExtensionsFromLocation(input);
|
|
457
|
+
let obsolete = {};
|
|
458
|
+
if (input.excludeObsolete && input.type === 1 ) {
|
|
459
|
+
try {
|
|
460
|
+
const raw = ( (await this.fileService.readFile(this.obsoleteFile)).value.toString());
|
|
461
|
+
obsolete = JSON.parse(raw);
|
|
462
|
+
}
|
|
463
|
+
catch (error) { }
|
|
464
|
+
}
|
|
465
|
+
return isEmptyObject(obsolete) ? extensions : extensions.filter(e => !obsolete[( ExtensionKey.create(e).toString())]);
|
|
466
|
+
}
|
|
467
|
+
async scanExtensionsFromLocation(input) {
|
|
468
|
+
const stat = await this.fileService.resolve(input.location);
|
|
469
|
+
if (!stat.children?.length) {
|
|
470
|
+
return [];
|
|
471
|
+
}
|
|
472
|
+
const extensions = await Promise.all(( stat.children.map(async (c) => {
|
|
473
|
+
if (!c.isDirectory) {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
if (input.type === 1 && basename$1(c.resource).indexOf('.') === 0) {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
const extensionScannerInput = ( new ExtensionScannerInput(
|
|
480
|
+
c.resource,
|
|
481
|
+
input.mtime,
|
|
482
|
+
input.applicationExtensionslocation,
|
|
483
|
+
input.applicationExtensionslocationMtime,
|
|
484
|
+
input.profile,
|
|
485
|
+
input.profileScanOptions,
|
|
486
|
+
input.type,
|
|
487
|
+
input.excludeObsolete,
|
|
488
|
+
input.validate,
|
|
489
|
+
input.productVersion,
|
|
490
|
+
input.productDate,
|
|
491
|
+
input.productCommit,
|
|
492
|
+
input.devMode,
|
|
493
|
+
input.language,
|
|
494
|
+
input.translations
|
|
495
|
+
));
|
|
496
|
+
return this.scanExtension(extensionScannerInput);
|
|
497
|
+
})));
|
|
498
|
+
return coalesce(extensions)
|
|
499
|
+
.sort((a, b) => a.location.path < b.location.path ? -1 : 1);
|
|
500
|
+
}
|
|
501
|
+
async scanExtensionsFromProfile(input) {
|
|
502
|
+
let profileExtensions = await this.scanExtensionsFromProfileResource(input.location, () => true, input);
|
|
503
|
+
if (input.applicationExtensionslocation && !this.uriIdentityService.extUri.isEqual(input.location, input.applicationExtensionslocation)) {
|
|
504
|
+
profileExtensions = profileExtensions.filter(e => !e.metadata?.isApplicationScoped);
|
|
505
|
+
const applicationExtensions = await this.scanExtensionsFromProfileResource(input.applicationExtensionslocation, (e) => !!e.metadata?.isBuiltin || !!e.metadata?.isApplicationScoped, input);
|
|
506
|
+
profileExtensions.push(...applicationExtensions);
|
|
507
|
+
}
|
|
508
|
+
return profileExtensions;
|
|
509
|
+
}
|
|
510
|
+
async scanExtensionsFromProfileResource(profileResource, filter, input) {
|
|
511
|
+
const scannedProfileExtensions = await this.extensionsProfileScannerService.scanProfileExtensions(profileResource, input.profileScanOptions);
|
|
512
|
+
if (!scannedProfileExtensions.length) {
|
|
513
|
+
return [];
|
|
514
|
+
}
|
|
515
|
+
const extensions = await Promise.all(( scannedProfileExtensions.map(async (extensionInfo) => {
|
|
516
|
+
if (filter(extensionInfo)) {
|
|
517
|
+
const extensionScannerInput = ( new ExtensionScannerInput(
|
|
518
|
+
extensionInfo.location,
|
|
519
|
+
input.mtime,
|
|
520
|
+
input.applicationExtensionslocation,
|
|
521
|
+
input.applicationExtensionslocationMtime,
|
|
522
|
+
input.profile,
|
|
523
|
+
input.profileScanOptions,
|
|
524
|
+
input.type,
|
|
525
|
+
input.excludeObsolete,
|
|
526
|
+
input.validate,
|
|
527
|
+
input.productVersion,
|
|
528
|
+
input.productDate,
|
|
529
|
+
input.productCommit,
|
|
530
|
+
input.devMode,
|
|
531
|
+
input.language,
|
|
532
|
+
input.translations
|
|
533
|
+
));
|
|
534
|
+
return this.scanExtension(extensionScannerInput, extensionInfo.metadata);
|
|
535
|
+
}
|
|
536
|
+
return null;
|
|
537
|
+
})));
|
|
538
|
+
return coalesce(extensions);
|
|
539
|
+
}
|
|
540
|
+
async scanOneOrMultipleExtensions(input) {
|
|
541
|
+
try {
|
|
542
|
+
if (await this.fileService.exists(joinPath(input.location, 'package.json'))) {
|
|
543
|
+
const extension = await this.scanExtension(input);
|
|
544
|
+
return extension ? [extension] : [];
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
return await this.scanExtensions(input);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
catch (error) {
|
|
551
|
+
this.logService.error(`Error scanning extensions at ${input.location.path}:`, getErrorMessage(error));
|
|
552
|
+
return [];
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
async scanExtension(input, metadata) {
|
|
556
|
+
try {
|
|
557
|
+
let manifest = await this.scanExtensionManifest(input.location);
|
|
558
|
+
if (manifest) {
|
|
559
|
+
if (!manifest.publisher) {
|
|
560
|
+
manifest.publisher = UNDEFINED_PUBLISHER;
|
|
561
|
+
}
|
|
562
|
+
metadata = metadata ?? manifest.__metadata;
|
|
563
|
+
delete manifest.__metadata;
|
|
564
|
+
const id = getGalleryExtensionId(manifest.publisher, manifest.name);
|
|
565
|
+
const identifier = metadata?.id ? { id, uuid: metadata.id } : { id };
|
|
566
|
+
const type = metadata?.isSystem ? 0 : input.type;
|
|
567
|
+
const isBuiltin = type === 0 || !!metadata?.isBuiltin;
|
|
568
|
+
manifest = await this.translateManifest(input.location, manifest, ExtensionScannerInput.createNlsConfiguration(input));
|
|
569
|
+
const extension = {
|
|
570
|
+
type,
|
|
571
|
+
identifier,
|
|
572
|
+
manifest,
|
|
573
|
+
location: input.location,
|
|
574
|
+
isBuiltin,
|
|
575
|
+
targetPlatform: metadata?.targetPlatform ?? "undefined" ,
|
|
576
|
+
metadata,
|
|
577
|
+
isValid: true,
|
|
578
|
+
validations: []
|
|
579
|
+
};
|
|
580
|
+
return input.validate ? this.validate(extension, input) : extension;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
catch (e) {
|
|
584
|
+
if (input.type !== 0 ) {
|
|
585
|
+
this.logService.error(e);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
590
|
+
validate(extension, input) {
|
|
591
|
+
let isValid = true;
|
|
592
|
+
const validations = validateExtensionManifest(input.productVersion, input.productDate, input.location, extension.manifest, extension.isBuiltin);
|
|
593
|
+
for (const [severity, message] of validations) {
|
|
594
|
+
if (severity === Severity$1.Error) {
|
|
595
|
+
isValid = false;
|
|
596
|
+
this.logService.error(this.formatMessage(input.location, message));
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
extension.isValid = isValid;
|
|
600
|
+
extension.validations = validations;
|
|
601
|
+
return extension;
|
|
602
|
+
}
|
|
603
|
+
async scanExtensionManifest(extensionLocation) {
|
|
604
|
+
const manifestLocation = joinPath(extensionLocation, 'package.json');
|
|
605
|
+
let content;
|
|
606
|
+
try {
|
|
607
|
+
content = ( (await this.fileService.readFile(manifestLocation)).value.toString());
|
|
608
|
+
}
|
|
609
|
+
catch (error) {
|
|
610
|
+
if (toFileOperationResult(error) !== 1 ) {
|
|
611
|
+
this.logService.error(this.formatMessage(extensionLocation, ( localizeWithPath(
|
|
612
|
+
'vs/platform/extensionManagement/common/extensionsScannerService',
|
|
613
|
+
'fileReadFail',
|
|
614
|
+
"Cannot read file {0}: {1}.",
|
|
615
|
+
manifestLocation.path,
|
|
616
|
+
error.message
|
|
617
|
+
))));
|
|
618
|
+
}
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
let manifest;
|
|
622
|
+
try {
|
|
623
|
+
manifest = JSON.parse(content);
|
|
624
|
+
}
|
|
625
|
+
catch (err) {
|
|
626
|
+
const errors = [];
|
|
627
|
+
parse(content, errors);
|
|
628
|
+
for (const e of errors) {
|
|
629
|
+
this.logService.error(this.formatMessage(extensionLocation, ( localizeWithPath(
|
|
630
|
+
'vs/platform/extensionManagement/common/extensionsScannerService',
|
|
631
|
+
'jsonParseFail',
|
|
632
|
+
"Failed to parse {0}: [{1}, {2}] {3}.",
|
|
633
|
+
manifestLocation.path,
|
|
634
|
+
e.offset,
|
|
635
|
+
e.length,
|
|
636
|
+
getParseErrorMessage(e.error)
|
|
637
|
+
))));
|
|
638
|
+
}
|
|
639
|
+
return null;
|
|
640
|
+
}
|
|
641
|
+
if (getNodeType(manifest) !== 'object') {
|
|
642
|
+
this.logService.error(this.formatMessage(extensionLocation, ( localizeWithPath(
|
|
643
|
+
'vs/platform/extensionManagement/common/extensionsScannerService',
|
|
644
|
+
'jsonParseInvalidType',
|
|
645
|
+
"Invalid manifest file {0}: Not an JSON object.",
|
|
646
|
+
manifestLocation.path
|
|
647
|
+
))));
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
return manifest;
|
|
651
|
+
}
|
|
652
|
+
async translateManifest(extensionLocation, extensionManifest, nlsConfiguration) {
|
|
653
|
+
const localizedMessages = await this.getLocalizedMessages(extensionLocation, extensionManifest, nlsConfiguration);
|
|
654
|
+
if (localizedMessages) {
|
|
655
|
+
try {
|
|
656
|
+
const errors = [];
|
|
657
|
+
const defaults = await this.resolveOriginalMessageBundle(localizedMessages.default, errors);
|
|
658
|
+
if (errors.length > 0) {
|
|
659
|
+
errors.forEach((error) => {
|
|
660
|
+
this.logService.error(this.formatMessage(extensionLocation, ( localizeWithPath(
|
|
661
|
+
'vs/platform/extensionManagement/common/extensionsScannerService',
|
|
662
|
+
'jsonsParseReportErrors',
|
|
663
|
+
"Failed to parse {0}: {1}.",
|
|
664
|
+
localizedMessages.default?.path,
|
|
665
|
+
getParseErrorMessage(error.error)
|
|
666
|
+
))));
|
|
667
|
+
});
|
|
668
|
+
return extensionManifest;
|
|
669
|
+
}
|
|
670
|
+
else if (getNodeType(localizedMessages) !== 'object') {
|
|
671
|
+
this.logService.error(this.formatMessage(extensionLocation, ( localizeWithPath(
|
|
672
|
+
'vs/platform/extensionManagement/common/extensionsScannerService',
|
|
673
|
+
'jsonInvalidFormat',
|
|
674
|
+
"Invalid format {0}: JSON object expected.",
|
|
675
|
+
localizedMessages.default?.path
|
|
676
|
+
))));
|
|
677
|
+
return extensionManifest;
|
|
678
|
+
}
|
|
679
|
+
const localized = localizedMessages.values || Object.create(null);
|
|
680
|
+
return localizeManifest(this.logService, extensionManifest, localized, defaults);
|
|
681
|
+
}
|
|
682
|
+
catch (error) {
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return extensionManifest;
|
|
686
|
+
}
|
|
687
|
+
async getLocalizedMessages(extensionLocation, extensionManifest, nlsConfiguration) {
|
|
688
|
+
const defaultPackageNLS = joinPath(extensionLocation, 'package.nls.json');
|
|
689
|
+
const reportErrors = (localized, errors) => {
|
|
690
|
+
errors.forEach((error) => {
|
|
691
|
+
this.logService.error(this.formatMessage(extensionLocation, ( localizeWithPath(
|
|
692
|
+
'vs/platform/extensionManagement/common/extensionsScannerService',
|
|
693
|
+
'jsonsParseReportErrors',
|
|
694
|
+
"Failed to parse {0}: {1}.",
|
|
695
|
+
localized?.path,
|
|
696
|
+
getParseErrorMessage(error.error)
|
|
697
|
+
))));
|
|
698
|
+
});
|
|
699
|
+
};
|
|
700
|
+
const reportInvalidFormat = (localized) => {
|
|
701
|
+
this.logService.error(this.formatMessage(extensionLocation, ( localizeWithPath(
|
|
702
|
+
'vs/platform/extensionManagement/common/extensionsScannerService',
|
|
703
|
+
'jsonInvalidFormat',
|
|
704
|
+
"Invalid format {0}: JSON object expected.",
|
|
705
|
+
localized?.path
|
|
706
|
+
))));
|
|
707
|
+
};
|
|
708
|
+
const translationId = `${extensionManifest.publisher}.${extensionManifest.name}`;
|
|
709
|
+
const translationPath = nlsConfiguration.translations[translationId];
|
|
710
|
+
if (translationPath) {
|
|
711
|
+
try {
|
|
712
|
+
const translationResource = URI.file(translationPath);
|
|
713
|
+
const content = ( (await this.fileService.readFile(translationResource)).value.toString());
|
|
714
|
+
const errors = [];
|
|
715
|
+
const translationBundle = parse(content, errors);
|
|
716
|
+
if (errors.length > 0) {
|
|
717
|
+
reportErrors(translationResource, errors);
|
|
718
|
+
return { values: undefined, default: defaultPackageNLS };
|
|
719
|
+
}
|
|
720
|
+
else if (getNodeType(translationBundle) !== 'object') {
|
|
721
|
+
reportInvalidFormat(translationResource);
|
|
722
|
+
return { values: undefined, default: defaultPackageNLS };
|
|
723
|
+
}
|
|
724
|
+
else {
|
|
725
|
+
const values = translationBundle.contents ? translationBundle.contents.package : undefined;
|
|
726
|
+
return { values: values, default: defaultPackageNLS };
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
catch (error) {
|
|
730
|
+
return { values: undefined, default: defaultPackageNLS };
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
else {
|
|
734
|
+
const exists = await this.fileService.exists(defaultPackageNLS);
|
|
735
|
+
if (!exists) {
|
|
736
|
+
return undefined;
|
|
737
|
+
}
|
|
738
|
+
let messageBundle;
|
|
739
|
+
try {
|
|
740
|
+
messageBundle = await this.findMessageBundles(extensionLocation, nlsConfiguration);
|
|
741
|
+
}
|
|
742
|
+
catch (error) {
|
|
743
|
+
return undefined;
|
|
744
|
+
}
|
|
745
|
+
if (!messageBundle.localized) {
|
|
746
|
+
return { values: undefined, default: messageBundle.original };
|
|
747
|
+
}
|
|
748
|
+
try {
|
|
749
|
+
const messageBundleContent = ( (await this.fileService.readFile(messageBundle.localized)).value.toString());
|
|
750
|
+
const errors = [];
|
|
751
|
+
const messages = parse(messageBundleContent, errors);
|
|
752
|
+
if (errors.length > 0) {
|
|
753
|
+
reportErrors(messageBundle.localized, errors);
|
|
754
|
+
return { values: undefined, default: messageBundle.original };
|
|
755
|
+
}
|
|
756
|
+
else if (getNodeType(messages) !== 'object') {
|
|
757
|
+
reportInvalidFormat(messageBundle.localized);
|
|
758
|
+
return { values: undefined, default: messageBundle.original };
|
|
759
|
+
}
|
|
760
|
+
return { values: messages, default: messageBundle.original };
|
|
761
|
+
}
|
|
762
|
+
catch (error) {
|
|
763
|
+
return { values: undefined, default: messageBundle.original };
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
async resolveOriginalMessageBundle(originalMessageBundle, errors) {
|
|
768
|
+
if (originalMessageBundle) {
|
|
769
|
+
try {
|
|
770
|
+
const originalBundleContent = ( (await this.fileService.readFile(originalMessageBundle)).value.toString());
|
|
771
|
+
return parse(originalBundleContent, errors);
|
|
772
|
+
}
|
|
773
|
+
catch (error) {
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
findMessageBundles(extensionLocation, nlsConfiguration) {
|
|
779
|
+
return ( new Promise((c, e) => {
|
|
780
|
+
const loop = (locale) => {
|
|
781
|
+
const toCheck = joinPath(extensionLocation, `package.nls.${locale}.json`);
|
|
782
|
+
this.fileService.exists(toCheck).then(exists => {
|
|
783
|
+
if (exists) {
|
|
784
|
+
c({ localized: toCheck, original: joinPath(extensionLocation, 'package.nls.json') });
|
|
785
|
+
}
|
|
786
|
+
const index = locale.lastIndexOf('-');
|
|
787
|
+
if (index === -1) {
|
|
788
|
+
c({ localized: joinPath(extensionLocation, 'package.nls.json'), original: null });
|
|
789
|
+
}
|
|
790
|
+
else {
|
|
791
|
+
locale = locale.substring(0, index);
|
|
792
|
+
loop(locale);
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
};
|
|
796
|
+
if (nlsConfiguration.devMode || nlsConfiguration.pseudo || !nlsConfiguration.language) {
|
|
797
|
+
return c({ localized: joinPath(extensionLocation, 'package.nls.json'), original: null });
|
|
798
|
+
}
|
|
799
|
+
loop(nlsConfiguration.language);
|
|
800
|
+
}));
|
|
801
|
+
}
|
|
802
|
+
formatMessage(extensionLocation, message) {
|
|
803
|
+
return `[${extensionLocation.path}]: ${message}`;
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
ExtensionsScanner = ( __decorate([
|
|
807
|
+
( __param(1, IExtensionsProfileScannerService)),
|
|
808
|
+
( __param(2, IUriIdentityService)),
|
|
809
|
+
( __param(3, IFileService)),
|
|
810
|
+
( __param(4, ILogService))
|
|
811
|
+
], ExtensionsScanner));
|
|
812
|
+
let CachedExtensionsScanner = class CachedExtensionsScanner extends ExtensionsScanner {
|
|
813
|
+
constructor(currentProfile, obsoleteFile, userDataProfilesService, extensionsProfileScannerService, uriIdentityService, fileService, logService) {
|
|
814
|
+
super(obsoleteFile, extensionsProfileScannerService, uriIdentityService, fileService, logService);
|
|
815
|
+
this.currentProfile = currentProfile;
|
|
816
|
+
this.userDataProfilesService = userDataProfilesService;
|
|
817
|
+
this.cacheValidatorThrottler = this._register(( new ThrottledDelayer(3000)));
|
|
818
|
+
this._onDidChangeCache = this._register(( new Emitter()));
|
|
819
|
+
this.onDidChangeCache = this._onDidChangeCache.event;
|
|
820
|
+
}
|
|
821
|
+
async scanExtensions(input) {
|
|
822
|
+
const cacheFile = this.getCacheFile(input);
|
|
823
|
+
const cacheContents = await this.readExtensionCache(cacheFile);
|
|
824
|
+
this.input = input;
|
|
825
|
+
if (cacheContents && cacheContents.input && ExtensionScannerInput.equals(cacheContents.input, this.input)) {
|
|
826
|
+
this.logService.debug('Using cached extensions scan result', ( input.location.toString()));
|
|
827
|
+
this.cacheValidatorThrottler.trigger(() => this.validateCache());
|
|
828
|
+
return ( cacheContents.result.map((extension) => {
|
|
829
|
+
extension.location = URI.revive(extension.location);
|
|
830
|
+
return extension;
|
|
831
|
+
}));
|
|
832
|
+
}
|
|
833
|
+
const result = await super.scanExtensions(input);
|
|
834
|
+
await this.writeExtensionCache(cacheFile, { input, result });
|
|
835
|
+
return result;
|
|
836
|
+
}
|
|
837
|
+
async readExtensionCache(cacheFile) {
|
|
838
|
+
try {
|
|
839
|
+
const cacheRawContents = await this.fileService.readFile(cacheFile);
|
|
840
|
+
const extensionCacheData = JSON.parse(( cacheRawContents.value.toString()));
|
|
841
|
+
return { result: extensionCacheData.result, input: revive(extensionCacheData.input) };
|
|
842
|
+
}
|
|
843
|
+
catch (error) {
|
|
844
|
+
this.logService.debug('Error while reading the extension cache file:', cacheFile.path, getErrorMessage(error));
|
|
845
|
+
}
|
|
846
|
+
return null;
|
|
847
|
+
}
|
|
848
|
+
async writeExtensionCache(cacheFile, cacheContents) {
|
|
849
|
+
try {
|
|
850
|
+
await this.fileService.writeFile(cacheFile, VSBuffer.fromString(JSON.stringify(cacheContents)));
|
|
851
|
+
}
|
|
852
|
+
catch (error) {
|
|
853
|
+
this.logService.debug('Error while writing the extension cache file:', cacheFile.path, getErrorMessage(error));
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
async validateCache() {
|
|
857
|
+
if (!this.input) {
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
const cacheFile = this.getCacheFile(this.input);
|
|
861
|
+
const cacheContents = await this.readExtensionCache(cacheFile);
|
|
862
|
+
if (!cacheContents) {
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
const actual = cacheContents.result;
|
|
866
|
+
const expected = JSON.parse(JSON.stringify(await super.scanExtensions(this.input)));
|
|
867
|
+
if (equals(expected, actual)) {
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
try {
|
|
871
|
+
this.logService.info('Invalidating Cache', actual, expected);
|
|
872
|
+
await this.fileService.del(cacheFile);
|
|
873
|
+
this._onDidChangeCache.fire();
|
|
874
|
+
}
|
|
875
|
+
catch (error) {
|
|
876
|
+
this.logService.error(error);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
getCacheFile(input) {
|
|
880
|
+
const profile = this.getProfile(input);
|
|
881
|
+
return this.uriIdentityService.extUri.joinPath(profile.cacheHome, input.type === 0 ? BUILTIN_MANIFEST_CACHE_FILE : USER_MANIFEST_CACHE_FILE);
|
|
882
|
+
}
|
|
883
|
+
getProfile(input) {
|
|
884
|
+
if (input.type === 0 ) {
|
|
885
|
+
return this.userDataProfilesService.defaultProfile;
|
|
886
|
+
}
|
|
887
|
+
if (!input.profile) {
|
|
888
|
+
return this.userDataProfilesService.defaultProfile;
|
|
889
|
+
}
|
|
890
|
+
if (this.uriIdentityService.extUri.isEqual(input.location, this.currentProfile.extensionsResource)) {
|
|
891
|
+
return this.currentProfile;
|
|
892
|
+
}
|
|
893
|
+
return this.userDataProfilesService.profiles.find(p => this.uriIdentityService.extUri.isEqual(input.location, p.extensionsResource)) ?? this.currentProfile;
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
CachedExtensionsScanner = ( __decorate([
|
|
897
|
+
( __param(2, IUserDataProfilesService)),
|
|
898
|
+
( __param(3, IExtensionsProfileScannerService)),
|
|
899
|
+
( __param(4, IUriIdentityService)),
|
|
900
|
+
( __param(5, IFileService)),
|
|
901
|
+
( __param(6, ILogService))
|
|
902
|
+
], CachedExtensionsScanner));
|
|
903
|
+
|
|
904
|
+
export { AbstractExtensionsScannerService, ExtensionScannerInput, Translations };
|