diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 3b59d2d692d..99553033cd2 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -384,7 +384,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio try { activationTimesBuilder.activateCallStart(); logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`); - const scope = typeof global === 'object' ? global : self; //todo@joh not so nice + const scope = typeof global === 'object' ? global : self; // `global` is nodejs while `self` is for workers const activateResult: Promise = extensionModule.activate.apply(scope, [context]); activationTimesBuilder.activateCallStop(); @@ -525,7 +525,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio } private async _doHandleExtensionTests(): Promise { - const { extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment; + const { extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment; if (!(extensionDevelopmentLocationURI && extensionTestsLocationURI && extensionTestsLocationURI.scheme === Schemas.file)) { return Promise.resolve(undefined); } diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index 30d15e39399..6f7f2b67feb 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -3,53 +3,96 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createApiFactoryAndRegisterActors } from 'vs/workbench/api/common/extHost.api.impl'; +import { createApiFactoryAndRegisterActors, IExtensionApiFactory } from 'vs/workbench/api/common/extHost.api.impl'; import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator'; import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { endsWith } from 'vs/base/common/strings'; +import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; +import * as vscode from 'vscode'; +import { TernarySearchTree } from 'vs/base/common/map'; import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; + +class ApiInstances { + + private readonly _apiInstances = new Map(); + + constructor( + private readonly _apiFactory: IExtensionApiFactory, + private readonly _extensionPaths: TernarySearchTree, + private readonly _extensionRegistry: ExtensionDescriptionRegistry, + private readonly _configProvider: ExtHostConfigProvider, + ) { + // + } + + get(modulePath: string): typeof vscode { + const extension = this._extensionPaths.findSubstr(modulePath) || nullExtensionDescription; + const id = ExtensionIdentifier.toKey(extension.identifier); + + let apiInstance = this._apiInstances.get(id); + if (!apiInstance) { + apiInstance = this._apiFactory(extension, this._extensionRegistry, this._configProvider); + this._apiInstances.set(id, apiInstance); + } + return apiInstance; + } +} export class ExtHostExtensionService extends AbstractExtHostExtensionService { + private _apiInstances?: ApiInstances; + protected async _beforeAlmostReadyToRunExtensions(): Promise { // initialize API and register actors - const factory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors); - - // globally define the vscode module and share that for all extensions - // todo@joh have an instance per extension, not a shared one.... - const sharedApiInstance = factory(nullExtensionDescription, this._registry, await this._extHostConfiguration.getConfigProvider()); - define('vscode', sharedApiInstance); + const apiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors); + const configProvider = await this._extHostConfiguration.getConfigProvider(); + const extensionPath = await this.getExtensionPathIndex(); + this._apiInstances = new ApiInstances(apiFactory, extensionPath, this._registry, configProvider); } protected _loadCommonJSModule(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { - // fake commonjs world + + // make sure modulePath ends with `.js` + const suffix = '.js'; + modulePath = endsWith(modulePath, suffix) ? modulePath : modulePath + suffix; + + interface FakeCommonJSSelf { + module?: object; + exports?: object; + require?: (module: string) => any; + window?: object; + __dirname: never; + __filename: never; + } + + // FAKE commonjs world that only collects exports + const patchSelf: FakeCommonJSSelf = self; const module = { exports: {} }; - //@ts-ignore - self['module'] = module; - //@ts-ignore - self['exports'] = module.exports; - // that's improper but might help extensions that aren't author correctly - // @ts-ignore - self['window'] = self; + patchSelf.module = module; + patchSelf.exports = module.exports; + patchSelf.window = self; // <- that's improper but might help extensions that aren't authored correctly + + // FAKE require function that only works for the vscode-module + patchSelf.require = (module: string) => { + if (module !== 'vscode') { + throw new Error(`Cannot load module '${module}'`); + } + return this._apiInstances!.get(modulePath); + }; try { activationTimesBuilder.codeLoadingStart(); - // import the single (!) script, make sure it's a JS-file - const suffix = '.js'; - if (endsWith(modulePath, suffix)) { - importScripts(modulePath); - } else { - importScripts(modulePath + suffix); - } + importScripts(modulePath); } finally { activationTimesBuilder.codeLoadingStop(); } - // return what it exported return Promise.resolve(module.exports as T); } - public async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise { + async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise { throw new Error('Not supported'); } }