extensionsScannerService.ts ×30

Frontier kind: Code frontier

unlabeled · c_e1411f5323ca

26 tests · 18496 LOC · 82 files · introduces 0 tests · 214 LOC · 4 files

Introduces — evidence that enters the hierarchy at this concept

Code
51 ranges214 lines · 4 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
2815 ranges18496 lines · 82 files · Browse complete extent
All tests (intent)
26 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

4 files ranked by introduced lines: 214 introduced LOC across 51 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/extensionManagement/common/extensionsScannerService.ts 163 introduced LOC · 30 ranges

Open complete file

108 }
109
110 > function getProductBuiltInExtensionsEnabledWithAutoUpdates(productService: IProductService, environmentService: IEnvironmentService): Set<string> { extensionsScannerService.ts
111 > const result = new Set<string>();
112 > for (const id of productService.builtInExtensionsEnabledWithAutoUpdates) {
113 const toLowerCaseId = id.toLowerCase();
114 if (environmentService.skipBuiltinExtensions?.some(skipId => skipId.toLowerCase() === toLowerCaseId)) {
117 result.add(toLowerCaseId);
118 }
119 > return result; extensionsScannerService.ts
120 > }
121
122 export type SystemExtensionsScanOptions = {
174
175 constructor(
176 > readonly systemExtensionsLocation: URI, extensionsScannerService.ts
177 > readonly userExtensionsLocation: URI,
178 > private readonly extensionsControlLocation: URI,
179 > currentProfile: IUserDataProfile,
180 > @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService,
181 > @IExtensionsProfileScannerService protected readonly extensionsProfileScannerService: IExtensionsProfileScannerService,
182 > @IFileService protected readonly fileService: IFileService,
183 > @ILogService protected readonly logService: ILogService,
184 > @IEnvironmentService private readonly environmentService: IEnvironmentService,
185 > @IProductService private readonly productService: IProductService,
186 > @IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
187 > @IInstantiationService private readonly instantiationService: IInstantiationService,
188 > ) {
189 > super();
190 >
191 > this.systemExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, currentProfile));
192 > this.userExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, currentProfile));
193 > this.extensionsScanner = this._register(this.instantiationService.createInstance(ExtensionsScanner));
194 >
195 > this._register(this.systemExtensionsCachedScanner.onDidChangeCache(() => this._onDidChangeCache.fire(ExtensionType.System)));
196 > this._register(this.userExtensionsCachedScanner.onDidChangeCache(() => this._onDidChangeCache.fire(ExtensionType.User)));
197 > }
198
199 private _targetPlatformPromise: Promise<TargetPlatform> | undefined;
486
487 private async createExtensionScannerInput(location: URI, profile: boolean, type: ExtensionType, language: string | undefined, validate: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined, productVersion: IProductVersion): Promise<ExtensionScannerInput> {
488 > const translations = await this.getTranslations(language ?? platform.language); extensionsScannerService.ts
489 > const mtime = await this.getMtime(location);
490 > const applicationExtensionsLocation = profile && !this.uriIdentityService.extUri.isEqual(location, this.userDataProfilesService.defaultProfile.extensionsResource) ? this.userDataProfilesService.defaultProfile.extensionsResource : undefined;
491 > const applicationExtensionsLocationMtime = applicationExtensionsLocation ? await this.getMtime(applicationExtensionsLocation) : undefined;
492 > return new ExtensionScannerInput(
493 > location,
494 > mtime,
495 > applicationExtensionsLocation,
496 > applicationExtensionsLocationMtime,
497 > profile,
498 > profileScanOptions,
499 > type,
500 > validate,
501 > productVersion.version,
502 > productVersion.date,
503 > this.productService.commit,
504 > !this.environmentService.isBuilt,
505 > language,
506 > translations,
507 > );
508 > }
509
510 private async getMtime(location: URI): Promise<number | undefined> {
512 > const stat = await this.fileService.stat(location);
513 > if (typeof stat.mtime === 'number') {
514 > return stat.mtime;
515 > }
516 > } catch (err) {
517 // That's ok...
518 }
519 return undefined;
521
522 private getProductVersion(): IProductVersion {
524 > version: this.productService.version,
525 > date: this.productService.date,
526 > };
527 > }
528
529 }
551
552 public static createNlsConfiguration(input: ExtensionScannerInput): NlsConfiguration {
554 > language: input.language,
555 > pseudo: input.language === 'pseudo',
556 > devMode: input.devMode,
557 > translations: input.translations
558 > };
559 > }
560
561 public static equals(a: ExtensionScannerInput, b: ExtensionScannerInput): boolean {
592
593 constructor(
594 > @IExtensionsProfileScannerService protected readonly extensionsProfileScannerService: IExtensionsProfileScannerService, extensionsScannerService.ts
595 > @IUriIdentityService protected readonly uriIdentityService: IUriIdentityService,
596 > @IFileService protected readonly fileService: IFileService,
597 > @IProductService productService: IProductService,
598 > @IEnvironmentService environmentService: IEnvironmentService,
599 > @ILogService protected readonly logService: ILogService
600 > ) {
601 > super();
602 > this.productQuality = productService.quality;
603 > this.productBuiltInExtensionsEnabledWithAutoUpdates = getProductBuiltInExtensionsEnabledWithAutoUpdates(productService, environmentService);
604 > }
605
606 async scanExtensions(input: ExtensionScannerInput): Promise<IRelaxedScannedExtension[]> {
675 async scanExtension(input: ExtensionScannerInput, scannedProfileExtension: IScannedProfileExtension): Promise<IRelaxedScannedExtension>;
676 async scanExtension(input: ExtensionScannerInput, scannedProfileExtension?: IScannedProfileExtension): Promise<IRelaxedScannedExtension | null> {
677 > const validations: [Severity, string][] = []; extensionsScannerService.ts
678 > let isValid = true;
679 > let manifest: IScannedExtensionManifest;
680 > try {
681 > manifest = await this.scanExtensionManifest(input.location);
682 > } catch (e) {
683 if (scannedProfileExtension) {
684 validations.push([Severity.Error, getErrorMessage(e)]);
698 }
699 }
701 > // allow publisher to be undefined to make the initial extension authoring experience smoother
702 > if (!manifest.publisher) {
703 manifest.publisher = UNDEFINED_PUBLISHER;
704 }
706 > let metadata: Metadata | undefined;
707 > if (scannedProfileExtension) {
708 metadata = {
709 ...scannedProfileExtension.metadata,
710 size: manifest.__metadata?.size,
711 };
712 > } else if (manifest.__metadata) { extensionsScannerService.ts
713 metadata = {
714 installedTimestamp: manifest.__metadata.installedTimestamp,
717 };
718 }
720 > delete manifest.__metadata;
721 > const id = getGalleryExtensionId(manifest.publisher, manifest.name);
722 > const identifier = metadata?.id ? { id, uuid: metadata.id } : { id };
723 > const type = metadata?.isSystem ? ExtensionType.System : input.type;
724 > const isBuiltin = type === ExtensionType.System || !!metadata?.isBuiltin;
725 > try {
726 > manifest = await this.translateManifest(input.location, manifest, ExtensionScannerInput.createNlsConfiguration(input));
727 > } catch (error) {
728 this.logService.warn('Failed to translate manifest', getErrorMessage(error));
729 }
730 > let extension: IRelaxedScannedExtension = { extensionsScannerService.ts
731 > type,
732 > identifier,
733 > manifest,
734 > location: input.location,
735 > isBuiltin,
736 > targetPlatform: metadata?.targetPlatform ?? TargetPlatform.UNDEFINED,
737 > publisherDisplayName: metadata?.publisherDisplayName,
738 > metadata,
739 > isValid,
740 > validations,
741 > preRelease: !!metadata?.preRelease,
742 > forceAutoUpdate: this.productBuiltInExtensionsEnabledWithAutoUpdates.has(id.toLowerCase()) && this.productQuality === 'stable',
743 > };
744 > if (input.validate) {
745 > extension = this.validate(extension, input);
746 > }
747 > if (manifest.enabledApiProposals) {
748 manifest.originalEnabledApiProposals = manifest.enabledApiProposals;
749 manifest.enabledApiProposals = parseEnabledApiProposalNames([...manifest.enabledApiProposals]);
750 }
751 > return extension; extensionsScannerService.ts
752 > }
753
754 validate(extension: IRelaxedScannedExtension, input: ExtensionScannerInput): IRelaxedScannedExtension {
755 > let isValid = extension.isValid; extensionsScannerService.ts
756 > const validations = validateExtensionManifest(input.productVersion, input.productDate, input.location, extension.manifest, extension.isBuiltin);
757 > for (const [severity, message] of validations) {
758 if (severity === Severity.Error) {
759 isValid = false;
761 }
762 }
763 > extension.isValid = isValid; extensionsScannerService.ts
764 > extension.validations = [...extension.validations, ...validations];
765 > return extension;
766 > }
767
768 private async scanExtensionManifest(extensionLocation: URI): Promise<IScannedExtensionManifest> {
769 > const manifestLocation = joinPath(extensionLocation, 'package.json'); extensionsScannerService.ts
770 > let content;
771 > try {
772 > content = (await this.fileService.readFile(manifestLocation)).value.toString();
773 > } catch (error) {
774 if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) {
775 this.logService.error(this.formatMessage(extensionLocation, localize('fileReadFail', "Cannot read file {0}: {1}.", manifestLocation.path, error.message)));
777 throw error;
778 }
779 > let manifest: IScannedExtensionManifest; extensionsScannerService.ts
780 > try {
781 > manifest = JSON.parse(content);
782 > } catch (err) {
783 // invalid JSON, let's get good errors
784 const errors: ParseError[] = [];
789 throw err;
790 }
791 > if (getNodeType(manifest) !== 'object') { extensionsScannerService.ts
792 const errorMessage = this.formatMessage(extensionLocation, localize('jsonParseInvalidType', "Invalid manifest file {0}: Not a JSON object.", manifestLocation.path));
793 this.logService.error(errorMessage);
794 throw new Error(errorMessage);
795 }
796 > return manifest; extensionsScannerService.ts
797 > }
798
799 private async translateManifest(extensionLocation: URI, extensionManifest: IExtensionManifest, nlsConfiguration: NlsConfiguration): Promise<IExtensionManifest> {
800 > const localizedMessages = await this.getLocalizedMessages(extensionLocation, extensionManifest, nlsConfiguration); extensionsScannerService.ts
801 > if (localizedMessages) {
802 try {
803 const errors: ParseError[] = [];
820 }
821 return extensionManifest;
823
824 private async getLocalizedMessages(extensionLocation: URI, extensionManifest: IExtensionManifest, nlsConfiguration: NlsConfiguration): Promise<LocalizedMessages | undefined> {
825 > const defaultPackageNLS = joinPath(extensionLocation, 'package.nls.json'); extensionsScannerService.ts
826 > const reportErrors = (localized: URI | null, errors: ParseError[]): void => {
827 errors.forEach((error) => {
828 this.logService.error(this.formatMessage(extensionLocation, localize('jsonsParseReportErrors', "Failed to parse {0}: {1}.", localized?.path, getParseErrorMessage(error.error))));
829 });
830 };
831 > const reportInvalidFormat = (localized: URI | null): void => { extensionsScannerService.ts
832 this.logService.error(this.formatMessage(extensionLocation, localize('jsonInvalidFormat', "Invalid format {0}: JSON object expected.", localized?.path)));
833 };
835 > const translationId = `${extensionManifest.publisher}.${extensionManifest.name}`;
836 > const translationPath = nlsConfiguration.translations[translationId];
837 >
838 > if (translationPath) {
839 try {
840 const translationResource = URI.file(translationPath);
855 return { values: undefined, default: defaultPackageNLS };
856 }
858 const exists = await this.fileService.exists(defaultPackageNLS);
859 if (!exists) {
950
951 constructor(
952 > private readonly currentProfile: IUserDataProfile, extensionsScannerService.ts
953 > @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService,
954 > @IExtensionsProfileScannerService extensionsProfileScannerService: IExtensionsProfileScannerService,
955 > @IUriIdentityService uriIdentityService: IUriIdentityService,
956 > @IFileService fileService: IFileService,
957 > @IProductService productService: IProductService,
958 > @IEnvironmentService environmentService: IEnvironmentService,
959 > @ILogService logService: ILogService
960 > ) {
961 > super(extensionsProfileScannerService, uriIdentityService, fileService, productService, environmentService, logService);
962 > }
963
964 override async scanExtensions(input: ExtensionScannerInput): Promise<IRelaxedScannedExtension[]> {
src/vs/platform/extensions/common/extensionValidator.ts 37 introduced LOC · 19 ranges

Open complete file

241
242 export function validateExtensionManifest(productVersion: string, productDate: ProductDate, extensionLocation: URI, extensionManifest: IExtensionManifest, extensionIsBuiltin: boolean): readonly [Severity, string][] {
243 > const validations: [Severity, string][] = []; extensionValidator.ts
244 > if (typeof extensionManifest.publisher !== 'undefined' && typeof extensionManifest.publisher !== 'string') {
245 validations.push([Severity.Error, nls.localize('extensionDescription.publisher', "property publisher must be of type `string`.")]);
246 return validations;
247 }
248 > if (typeof extensionManifest.name !== 'string') { extensionValidator.ts
249 validations.push([Severity.Error, nls.localize('extensionDescription.name', "property `{0}` is mandatory and must be of type `string`", 'name')]);
250 return validations;
251 }
252 > if (typeof extensionManifest.version !== 'string') { extensionValidator.ts
253 validations.push([Severity.Error, nls.localize('extensionDescription.version', "property `{0}` is mandatory and must be of type `string`", 'version')]);
254 return validations;
255 }
256 > if (!extensionManifest.engines) { extensionValidator.ts
257 validations.push([Severity.Error, nls.localize('extensionDescription.engines', "property `{0}` is mandatory and must be of type `object`", 'engines')]);
258 return validations;
259 }
260 > if (typeof extensionManifest.engines.vscode !== 'string') { extensionValidator.ts
261 validations.push([Severity.Error, nls.localize('extensionDescription.engines.vscode', "property `{0}` is mandatory and must be of type `string`", 'engines.vscode')]);
262 return validations;
263 }
264 > if (typeof extensionManifest.extensionDependencies !== 'undefined') { extensionValidator.ts
265 if (!isStringArray(extensionManifest.extensionDependencies)) {
266 validations.push([Severity.Error, nls.localize('extensionDescription.extensionDependencies', "property `{0}` can be omitted or must be of type `string[]`", 'extensionDependencies')]);
268 }
269 }
270 > if (typeof extensionManifest.extensionAffinity !== 'undefined') { extensionValidator.ts
271 if (!isStringArray(extensionManifest.extensionAffinity)) {
272 validations.push([Severity.Error, nls.localize('extensionDescription.extensionAffinity', "property `{0}` can be omitted or must be of type `string[]`", 'extensionAffinity')]);
274 }
275 }
276 > if (typeof extensionManifest.activationEvents !== 'undefined') { extensionValidator.ts
277 > if (!isStringArray(extensionManifest.activationEvents)) {
278 validations.push([Severity.Error, nls.localize('extensionDescription.activationEvents1', "property `{0}` can be omitted or must be of type `string[]`", 'activationEvents')]);
279 return validations;
280 }
281 > if (typeof extensionManifest.main === 'undefined' && typeof extensionManifest.browser === 'undefined') { extensionValidator.ts
282 validations.push([Severity.Error, nls.localize('extensionDescription.activationEvents2', "property `{0}` should be omitted if the extension doesn't have a `{1}` or `{2}` property.", 'activationEvents', 'main', 'browser')]);
283 return validations;
284 }
286 > if (typeof extensionManifest.extensionKind !== 'undefined') {
287 if (typeof extensionManifest.main === 'undefined') {
288 validations.push([Severity.Warning, nls.localize('extensionDescription.extensionKind', "property `{0}` can be defined only if property `main` is also defined.", 'extensionKind')]);
290 }
291 }
292 > if (typeof extensionManifest.main !== 'undefined') { extensionValidator.ts
293 > if (typeof extensionManifest.main !== 'string') {
294 validations.push([Severity.Error, nls.localize('extensionDescription.main1', "property `{0}` can be omitted or must be of type `string`", 'main')]);
295 return validations;
296 > } else { extensionValidator.ts
297 > const mainLocation = joinPath(extensionLocation, extensionManifest.main);
298 > if (!isEqualOrParent(mainLocation, extensionLocation)) {
299 validations.push([Severity.Warning, nls.localize('extensionDescription.main2', "Expected `main` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", mainLocation.path, extensionLocation.path)]);
300 // not a failure case
301 }
303 > }
304 > if (typeof extensionManifest.browser !== 'undefined') {
305 if (typeof extensionManifest.browser !== 'string') {
306 validations.push([Severity.Error, nls.localize('extensionDescription.browser1', "property `{0}` can be omitted or must be of type `string`", 'browser')]);
314 }
315 }
317 > if (!semver.valid(extensionManifest.version)) {
318 validations.push([Severity.Error, nls.localize('notSemver', "Extension version is not semver compatible.")]);
319 return validations;
320 }
322 > const notices: string[] = [];
323 > const validExtensionVersion = isValidExtensionVersion(productVersion, productDate, extensionManifest, extensionIsBuiltin, notices);
324 > if (!validExtensionVersion) {
325 for (const notice of notices) {
326 validations.push([Severity.Error, notice]);
327 }
328 }
330 > return validations;
331 > }
332
333 export function isValidExtensionVersion(productVersion: string, productDate: ProductDate, extensionManifest: IExtensionManifest, extensionIsBuiltin: boolean, notices: string[]): boolean {
379 }
380
381 > function isStringArray(arr: readonly string[]): boolean { extensionValidator.ts
382 > if (!Array.isArray(arr)) {
383 return false;
384 }
385 > for (let i = 0, len = arr.length; i < len; i++) { extensionValidator.ts
386 > if (typeof arr[i] !== 'string') {
387 return false;
388 }
390 > return true;
391 > }
src/vs/platform/extensionManagement/node/extensionsProfileScannerService.ts 8 introduced LOC · 1 range

Open complete file

14 export class ExtensionsProfileScannerService extends AbstractExtensionsProfileScannerService {
15 constructor(
16 > @INativeEnvironmentService environmentService: INativeEnvironmentService, extensionsProfileScannerService.ts
17 > @IFileService fileService: IFileService,
18 > @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService,
19 > @IUriIdentityService uriIdentityService: IUriIdentityService,
20 > @ILogService logService: ILogService,
21 > ) {
22 > super(URI.file(environmentService.extensionsPath), fileService, userDataProfilesService, uriIdentityService, logService);
23 > }
24 }
src/vs/platform/files/common/fileService.ts 6 introduced LOC · 1 range

Open complete file

312
313 async stat(resource: URI): Promise<IFileStatWithPartialMetadata> {
314 > const provider = await this.withProvider(resource); fileService.ts
315 >
316 > const stat = await provider.stat(resource);
317 >
318 > return this.toFileStat(provider, resource, stat, undefined, true, () => false /* Do not resolve any children */);
319 > }
320
321 async realpath(resource: URI): Promise<URI | undefined> {