Add support for web worker extensions to define their entry point via "browser"

This commit is contained in:
Alex Dima 2020-07-24 10:40:39 +02:00
parent 98e653e82c
commit a7b1e06283
No known key found for this signature in database
GPG key ID: 6E58D7B045760DA0
9 changed files with 39 additions and 20 deletions

View file

@ -154,8 +154,6 @@ async function getExtensionPackageJSON(extensionPath) {
}
if (packageJSON.browser) {
packageJSON.main = packageJSON.browser;
let mainFilePath = path.join(extensionPath, packageJSON.browser);
if (path.extname(mainFilePath) !== '.js') {
mainFilePath += '.js';

View file

@ -193,8 +193,6 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
}
}
protected abstract _beforeAlmostReadyToRunExtensions(): Promise<void>;
public async deactivateAll(): Promise<void> {
let allPromises: Promise<void>[] = [];
try {
@ -254,7 +252,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
if (!this._extensionPathIndex) {
const tree = TernarySearchTree.forPaths<IExtensionDescription>();
const extensions = this._registry.getAllExtensionDescriptions().map(ext => {
if (!ext.main) {
if (!this._getEntryPoint(ext)) {
return undefined;
}
return this._hostUtils.realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
@ -345,7 +343,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
const event = getTelemetryActivationEvent(extensionDescription, reason);
type ActivatePluginClassification = {} & TelemetryActivationEventFragment;
this._mainThreadTelemetryProxy.$publicLog2<TelemetryActivationEvent, ActivatePluginClassification>('activatePlugin', event);
if (!extensionDescription.main) {
const entryPoint = this._getEntryPoint(extensionDescription);
if (!entryPoint) {
// Treat the extension as being empty => NOT AN ERROR CASE
return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE));
}
@ -355,15 +354,13 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
return Promise.all([
this._loadCommonJSModule<IExtensionModule>(joinPath(extensionDescription.extensionLocation, extensionDescription.main), activationTimesBuilder),
this._loadCommonJSModule<IExtensionModule>(joinPath(extensionDescription.extensionLocation, entryPoint), activationTimesBuilder),
this._loadExtensionContext(extensionDescription)
]).then(values => {
return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder);
});
}
protected abstract _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;
private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise<vscode.ExtensionContext> {
const globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage);
@ -747,6 +744,9 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
this._onDidChangeRemoteConnectionData.fire();
}
protected abstract _beforeAlmostReadyToRunExtensions(): Promise<void>;
protected abstract _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined;
protected abstract _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;
public abstract async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
}

View file

@ -13,6 +13,7 @@ import { ExtHostDownloadService } from 'vs/workbench/api/node/extHostDownloadSer
import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
class NodeModuleRequireInterceptor extends RequireInterceptor {
@ -76,6 +77,10 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
};
}
protected _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined {
return extensionDescription.main;
}
protected _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
if (module.scheme !== Schemas.file) {
throw new Error(`Cannot load URI: '${module}', must be of file-scheme`);

View file

@ -8,6 +8,7 @@ import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHost
import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { URI } from 'vs/base/common/uri';
import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
class WorkerRequireInterceptor extends RequireInterceptor {
@ -40,6 +41,10 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
await this._fakeModules.install();
}
protected _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined {
return extensionDescription.browser;
}
protected async _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
module = module.with({ path: ensureSuffix(module.path, '.js') });

View file

@ -157,7 +157,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
this._extensionService.getExtensions().then((extensions) => {
// We only deal with extensions with source code!
this._extensionsDescriptions = extensions.filter((extension) => {
return !!extension.main;
return Boolean(extension.main) || Boolean(extension.browser);
});
this._updateExtensions();
});

View file

@ -64,7 +64,7 @@ class DefaultFormatter extends Disposable implements IWorkbenchContribution {
DefaultFormatter.extensionDescriptions.push(nls.localize('nullFormatterDescription', "None"));
for (const extension of extensions) {
if (extension.main) {
if (extension.main || extension.browser) {
DefaultFormatter.extensionIds.push(extension.identifier.value);
DefaultFormatter.extensionDescriptions.push(extension.description || '');
}

View file

@ -204,8 +204,8 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
const [telemetryInfo, remoteInitData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]);
// Collect all identifiers for extension ids which can be considered "resolved"
const resolvedExtensions = remoteInitData.allExtensions.filter(extension => !extension.main).map(extension => extension.identifier);
const hostExtensions = remoteInitData.allExtensions.filter(extension => extension.main && extension.api === 'none').map(extension => extension.identifier);
const resolvedExtensions = remoteInitData.allExtensions.filter(extension => !extension.main && !extension.browser).map(extension => extension.identifier);
const hostExtensions = remoteInitData.allExtensions.filter(extension => (extension.main || extension.browser) && extension.api === 'none').map(extension => extension.identifier);
const workspace = this._contextService.getWorkspace();
return {
commit: this._productService.commit,

View file

@ -706,10 +706,6 @@ function determineRunningLocation(productService: IProductService, configuration
}
if (extensionKind === 'web' && isInstalledLocally && hasLocalWebWorker) {
// web worker extensions run in the local web worker if possible
if (typeof extension.browser !== 'undefined') {
// The "browser" field determines the entry point
(<any>extension).main = extension.browser;
}
return ExtensionRunningLocation.LocalWebWorker;
}
}

View file

@ -389,9 +389,8 @@ class ExtensionManifestValidator extends ExtensionManifestHandler {
notices.push(nls.localize('extensionDescription.main1', "property `{0}` can be omitted or must be of type `string`", 'main'));
return false;
} else {
let normalizedAbsolutePath = path.join(extensionFolderPath, extensionDescription.main);
if (normalizedAbsolutePath.indexOf(extensionFolderPath)) {
const normalizedAbsolutePath = path.join(extensionFolderPath, extensionDescription.main);
if (!normalizedAbsolutePath.startsWith(extensionFolderPath)) {
notices.push(nls.localize('extensionDescription.main2', "Expected `main` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", normalizedAbsolutePath, extensionFolderPath));
// not a failure case
}
@ -401,6 +400,22 @@ class ExtensionManifestValidator extends ExtensionManifestHandler {
return false;
}
}
if (typeof extensionDescription.browser !== 'undefined') {
if (typeof extensionDescription.browser !== 'string') {
notices.push(nls.localize('extensionDescription.browser1', "property `{0}` can be omitted or must be of type `string`", 'browser'));
return false;
} else {
const normalizedAbsolutePath = path.join(extensionFolderPath, extensionDescription.browser);
if (!normalizedAbsolutePath.startsWith(extensionFolderPath)) {
notices.push(nls.localize('extensionDescription.browser2', "Expected `browser` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", normalizedAbsolutePath, extensionFolderPath));
// not a failure case
}
}
if (typeof extensionDescription.activationEvents === 'undefined') {
notices.push(nls.localize('extensionDescription.browser3', "properties `{0}` and `{1}` must both be specified or must both be omitted", 'activationEvents', 'browser'));
return false;
}
}
return true;
}