src/vs/platform/extensionManagement/common/extensionsScannerService.ts/ExtensionsScanner/scanExtensions
class ExtensionsScanner:	async scanExtensions(input: ExtensionScannerInput): Promise<IRelaxedScannedExtension[]> {
		const stat = await this.fileService.resolve(input.location);
		if (stat.children) {
			let obsolete: IStringDictionary<boolean> = {};
			if (input.excludeObsolete && input.type === ExtensionType.User) {
				try {
					const raw = (await this.fileService.readFile(joinPath(input.location, '.obsolete'))).value.toString();
					obsolete = JSON.parse(raw);
				} catch (error) { /* ignore */ }
			}
			const extensions = await Promise.all<IRelaxedScannedExtension | null>(
				stat.children.map(async c => {
					if (!c.isDirectory) {
						return null;
					}
					// Do not consider user extension folder starting with `.`
					if (input.type === ExtensionType.User && basename(c.resource).indexOf('.') === 0) {
						return null;
					}
					const extensionScannerInput = new ExtensionScannerInput(c.resource, input.mtime, input.type, input.excludeObsolete, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations);
					const extension = await this.scanExtension(extensionScannerInput);
					return extension && !obsolete[ExtensionKey.create(extension).toString()] ? extension : null;
				}));
			return coalesce(extensions);
		}
		return [];
	}