Skip to content

Commit

Permalink
eclipse-che/che#11306 add doc and comments
Browse files Browse the repository at this point in the history
Signed-off-by: Yevhen Vydolob <yvydolob@redhat.com>
  • Loading branch information
evidolob committed Nov 26, 2018
1 parent ae71d04 commit 85a4f7b
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 1 deletion.
110 changes: 110 additions & 0 deletions packages/plugin-ext/doc/how-to-add-new-plugin-namespace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# This document how to add new plugin api namespace

New Plugin API namespace should be packaged as Theia extension

## Provide your API or namespace

We assuming that you provide your API as separate npm package.
In that package you can declare your api.
Example `foo.d.ts`:

```typescript
declare module '@bar/foo' {
export namespace fooBar {
export function getFoo(): Foo;
}
}
```

## Declare `ExtPluginApiProvider` implementation

```typescript
@injectable()
export class FooPluginApiProvider implements ExtPluginApiProvider {
provideApi(): ExtPluginApi {
return {
frontendExtApi: {
initPath: '/path/to/foo/api/implementation.js',
initFunction: 'fooInitializationFunction',
initVariable: 'foo_global_variable'
},
backendInitPath: path.join(__dirname, 'path/to/backend/foo/implementation.js')
};
}
}
```

## Then you need to register `FooPluginApiProvider`, add next sample in your backend module:

```typescript
bind(FooPluginApiProvider).toSelf().inSingletonScope();
bind(Symbol.for(ExtPluginApiProvider)).toService(FooPluginApiProvider);
```

## Next you need to implement `ExtPluginApiBackendInitializationFn`, which should hanlde `@bar/foo` module loading and instantiate `@foo/bar` API object, `path/to/backend/foo/implementation.js` example :

```typescript
export const provideApi: ExtPluginApiBackendInitializationFn = (rpc: RPCProtocol, pluginManager: PluginManager) => {
cheApiFactory = createAPIFactory(rpc);
plugins = pluginManager;

if (!isLoadOverride) {
overrideInternalLoad();
isLoadOverride = true;
}

};

function overrideInternalLoad(): void {
const module = require('module');
const internalLoad = module._load;

module._load = function (request: string, parent: any, isMain: {}) {
if (request !== '@bar/foo') {
return internalLoad.apply(this, arguments);
}

const plugin = findPlugin(parent.filename);
if (plugin) {
let apiImpl = pluginsApiImpl.get(plugin.model.id);
if (!apiImpl) {
apiImpl = cheApiFactory(plugin);
pluginsApiImpl.set(plugin.model.id, apiImpl);
}
return apiImpl;
}

if (!defaultApi) {
console.warn(`Could not identify plugin for '@bar/foo' require call from ${parent.filename}`);
defaultApi = cheApiFactory(emptyPlugin);
}

return defaultApi;
};
}

function findPlugin(filePath: string): Plugin | undefined {
return plugins.getAllPlugins().find(plugin => filePath.startsWith(plugin.pluginFolder));
}
```

## Next you need to implement `createAPIFactory` factory function

Example:

```typescript
import * as fooApi from '@bar/foo';
export function createAPIFactory(rpc: RPCProtocol): ApiFactory {
const fooBarImpl = new FooBarImpl(rpc);
return function (plugin: Plugin): typeof fooApi {
const FooBar: typeof fooApi.fooBar = {
getFoo(): fooApi.Foo{
return fooBarImpl.getFooImpl();
}
}
return <typeof fooApi>{
fooBar : FooBar
};
}

```
32 changes: 31 additions & 1 deletion packages/plugin-ext/src/common/plugin-ext-api-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,31 @@ import { PluginManager, Plugin } from '../api/plugin-api';
import { interfaces } from 'inversify';

export const ExtPluginApiProvider = 'extPluginApi';
/**
* Provider for extension API description
*/
export interface ExtPluginApiProvider {
/**
* Provide API description
*/
provideApi(): ExtPluginApi;
}

/**
* Plugin API extension description.
* This inteface describes scripts for both plugin runtimes: frontend(WebWorker) and backend(NodeJs)
*/
export interface ExtPluginApi {

/**
* Path to the script which should do some initialization before backend plugin is loaded.
* Path to the script which should be loaded to provide api, module should export `provideApi` function with
* [ExtPluginApiBackendInitializationFn](#ExtPluginApiBackendInitializationFn) signature
*/
backendInitPath?: string;

/**
* Initialization information for frontend part of Plugin API
*/
frontendExtApi?: FrontendExtPluginApi;
}

Expand All @@ -40,13 +54,29 @@ export interface ExtPluginApiBackendInitializationFn {
(rpc: RPCProtocol, pluginManager: PluginManager): void;
}

/**
* Interface contains information for frontend(WebWorker) Plugin API extension initialization
*/
export interface FrontendExtPluginApi {
/**
* path to js file
*/
initPath: string;
/** global variable name */
initVariable: string;
/**
* init function name,
* function should have [ExtPluginApiFrontendInitializationFn](#ExtPluginApiFrontendInitializationFn)
*/
initFunction: string;
}

export const MainPluginApiProvider = Symbol('mainPluginApi');

/**
* Implementation should contains main(Theia) part of new namespace in Plugin API.
* [initialize](#initialize) will be called once per plugin runtime
*/
export interface MainPluginApiProvider {
initialize(rpc: RPCProtocol, container: interfaces.Container): void;
}

0 comments on commit 85a4f7b

Please sign in to comment.