Skip to content

Commit e5b38cf

Browse files
committed
Add custom marketplace override. Touch up UI when using Open VSX.
1 parent 8157ad6 commit e5b38cf

File tree

3 files changed

+79
-9
lines changed

3 files changed

+79
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/* eslint-disable header/header */
2+
/*---------------------------------------------------------------------------------------------
3+
* Copyright (c) Coder Technologies. All rights reserved.
4+
* Licensed under the MIT License. See License.txt in the project root for license information.
5+
*--------------------------------------------------------------------------------------------*/
6+
7+
import { IProductConfiguration } from 'vs/base/common/product';
8+
9+
/**
10+
* Allows for overriding the extension gallery configuration via the
11+
* `'EXTENSIONS_GALLERY'` environment variable.
12+
*
13+
* @example
14+
* ```sh
15+
* export EXTENSIONS_GALLERY='{"serviceUrl": "https://extensions.coder.com/api"}'
16+
* ```
17+
*/
18+
export function parseExtensionsGalleryEnv(extensionsGalleryEnv: string): IProductConfiguration['extensionsGallery'] {
19+
return {
20+
serviceUrl: '',
21+
itemUrl: '',
22+
resourceUrlTemplate: '',
23+
controlUrl: '',
24+
recommendationsUrl: '',
25+
...JSON.parse(extensionsGalleryEnv),
26+
};
27+
}

src/vs/platform/product/common/product.ts

+18
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { env } from 'vs/base/common/process';
99
import { IProductConfiguration } from 'vs/base/common/product';
1010
import { dirname, joinPath } from 'vs/base/common/resources';
1111
import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes';
12+
import { parseExtensionsGalleryEnv } from 'vs/platform/product/common/marketplace';
1213

1314
/**
1415
* @deprecated You MUST use `IProductService` if possible.
@@ -76,6 +77,23 @@ else {
7677
}
7778
}
7879

80+
if (typeof env['EXTENSIONS_GALLERY'] !== 'undefined') {
81+
console.log(`Custom marketplace env found. Parsing...`);
82+
83+
try {
84+
Object.assign(product, {
85+
extensionsGallery: parseExtensionsGalleryEnv(env['EXTENSIONS_GALLERY'])
86+
});
87+
} catch (error) {
88+
console.error(error);
89+
console.info('Check that your env var is valid JSON and conforms to `IProductConfiguration[\'extensionsGallery\']`>');
90+
}
91+
92+
console.log(`Custom marketplace enabled.`);
93+
console.log(JSON.stringify(product.extensionsGallery, null, 2));
94+
}
95+
96+
7997
/**
8098
* @deprecated You MUST use `IProductService` if possible.
8199
*/

src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts

+34-9
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ import { installLocalInRemoteIcon } from 'vs/workbench/contrib/extensions/browse
5959
import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions';
6060
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
6161
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
62+
import { IProductService } from 'vs/platform/product/common/productService';
63+
import { memoize } from 'vs/base/common/decorators';
6264

6365
const SearchMarketplaceExtensionsContext = new RawContextKey<boolean>('searchMarketplaceExtensions', false);
6466
const SearchIntalledExtensionsContext = new RawContextKey<boolean>('searchInstalledExtensions', false);
@@ -73,17 +75,21 @@ const SearchUnsupportedWorkspaceExtensionsContext = new RawContextKey<boolean>('
7375
const RecommendedExtensionsContext = new RawContextKey<boolean>('recommendedExtensions', false);
7476

7577
export class ExtensionsViewletViewsContribution implements IWorkbenchContribution {
78+
static EXTENSION_RECOMMENDATION_KEY = 'EXTENSION_RECOMMENDATION_KEY';
7679

7780
private readonly container: ViewContainer;
7881

7982
constructor(
8083
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
8184
@ILabelService private readonly labelService: ILabelService,
8285
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
83-
@IContextKeyService private readonly contextKeyService: IContextKeyService
86+
@IContextKeyService private readonly contextKeyService: IContextKeyService,
87+
@IProductService private readonly _productService: IProductService,
8488
) {
8589
this.container = viewDescriptorService.getViewContainerById(VIEWLET_ID)!;
8690
this.registerViews();
91+
92+
this.contextKeyService.createKey(ExtensionsViewletViewsContribution.EXTENSION_RECOMMENDATION_KEY, !!this._productService.extensionsGallery?.recommendationsUrl);
8793
}
8894

8995
private registerViews(): void {
@@ -226,16 +232,22 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
226232
canToggleVisibility: false
227233
});
228234

235+
229236
/*
230-
* Default recommended extensions view
231-
* When user has installed extensions, this is shown along with the views for enabled & disabled extensions
232-
* When user has no installed extensions, this is shown along with the view for popular extensions
233-
*/
237+
* Default recommended extensions view
238+
* When user has installed extensions, this is shown along with the views for enabled & disabled extensions
239+
* When user has no installed extensions, this is shown along with the view for popular extensions
240+
*/
234241
viewDescriptors.push({
235242
id: 'extensions.recommendedList',
236243
name: localize('recommendedExtensions', "Recommended"),
237244
ctorDescriptor: new SyncDescriptor(DefaultRecommendedExtensionsView, [{}]),
238-
when: ContextKeyExpr.and(DefaultViewsContext, ContextKeyExpr.not('config.extensions.showRecommendationsOnlyOnDemand')),
245+
when: ContextKeyExpr.and(
246+
DefaultViewsContext,
247+
ContextKeyExpr.not('config.extensions.showRecommendationsOnlyOnDemand'),
248+
/** @coder Open VSX doesn't yet support `recommendationsUrl`, so we hide the pane. */
249+
ContextKeyExpr.has(ExtensionsViewletViewsContribution.EXTENSION_RECOMMENDATION_KEY)
250+
),
239251
weight: 40,
240252
order: 3,
241253
canToggleVisibility: true
@@ -479,7 +491,9 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
479491
@IExtensionService extensionService: IExtensionService,
480492
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
481493
@IPreferencesService private readonly preferencesService: IPreferencesService,
482-
@ICommandService private readonly commandService: ICommandService
494+
@ICommandService private readonly commandService: ICommandService,
495+
@IProductService private readonly _productService: IProductService,
496+
483497
) {
484498
super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService);
485499

@@ -499,7 +513,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
499513
this.recommendedExtensionsContextKey = RecommendedExtensionsContext.bindTo(contextKeyService);
500514
this._register(this.paneCompositeService.onDidPaneCompositeOpen(e => { if (e.viewContainerLocation === ViewContainerLocation.Sidebar) { this.onViewletOpen(e.composite); } }, this));
501515
this.searchViewletState = this.getMemento(StorageScope.WORKSPACE, StorageTarget.USER);
502-
516+
503517
if (extensionManagementServerService.webExtensionManagementServer) {
504518
this._register(extensionsWorkbenchService.onChange(() => {
505519
// show installed web extensions view only when it is not visible
@@ -511,6 +525,17 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
511525
}
512526
}
513527

528+
/** @coder Added to clarify source of marketplace extensions. */
529+
@memoize
530+
get marketplaceHostname(): string {
531+
if (this._productService.extensionsGallery?.serviceUrl) {
532+
return new URL(this._productService.extensionsGallery?.serviceUrl).hostname;
533+
}
534+
535+
return 'Marketplace';
536+
}
537+
538+
514539
get searchValue(): string | undefined {
515540
return this.searchBox?.getValue();
516541
}
@@ -525,7 +550,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
525550
hide(overlay);
526551

527552
const header = append(this.root, $('.header'));
528-
const placeholder = localize('searchExtensions', "Search Extensions in Marketplace");
553+
const placeholder = localize('searchExtensions', 'Search Extensions on {0}', this.marketplaceHostname);
529554

530555
const searchValue = this.searchViewletState['query.value'] ? this.searchViewletState['query.value'] : '';
531556

0 commit comments

Comments
 (0)