How to Design Modules
Based on the OpenSumi system, this section mainly introduces the idea of building modules, aiming to extend native framework functions. The creation of modules and specific practices can be found in our classic case, which contains basic ideas and practices to create specific modules.
Know about Dependency Injection
Before learning the module, we recommend you to read Dependency Injection. In OpenSumi, all service registrations and invocations are based on this unified dependency injection structure to decouple service implementation and invocation logic, allowing framework developers to focus on developing modules and achieving more independent module construction.
What Is a Module?
Generally speaking, modules are code blocks that rely on the OpenSumi framework to extend native capabilities by using BrowserModule
and NodeModule
.
Taking the extension module of the Browser
layer as an example, a BrowserModule
basic format is defined as follows:
import { Provider, Injectable } from '@opensumi/di';
import { BrowserModule } from '@opensumi/ide-core-browser';
@Injectable()
export class ExplorerModule extends BrowserModule {
providers: Provider[] = [
AnyContributions, // contribution point files
AnyService // register additional services
];
}
Contribution point files provide registration of capabilities, such as Command
, Menu
, Keybinding
and ComponentView
.
And some other Service definitions.
How to Encode
When you first start to learn about OpenSumi module coding, we recommend that you look at OpenSumi first to see if there is any type of functionality or layout, and then refer to the source code to do the relevant coding. This can be accomplished with half the effort.
To start with basic needs, module coding can be generally divided into following two categories:
- Functional requirements based on a view
- Demand based on service capability
Requirements Based on a View
The first step for all view requirements is to create a view, and in the OpenSumi framework, the steps to create a view can be divided into two steps:
- Register the view module
- Introduce the module
- Used under the specific
Location (Layout Block)
case
Using the 'Explorer' module as an example, we create a explorer.contribution.ts
file to register a view container:
@Domain(ComponentContribution)
export class ExplorerContribution implements ComponentContribution {
registerComponent(registry: ComponentRegistry) {
registry.register('@opensumi/ide-explorer', [], {
iconClass: getIcon('explorer'),
title: localize('explorer.title'),
priority: 10,
// component: ExplorerComponent, // The specific rendering component can be passed in here
containerId: EXPLORER_CONTAINER_ID
});
}
}
When registering a view component, you can also import a specific rendering component so that the view block will be rendered with that component, for example, the Search
panel is registered with the corresponding component directly, as shown here.
If you want to register a drawerlayout that can hold multiple view components in the left and right sidebar, you can leave it here and then unregister the view in another module, such as the drawerlayout in Explorer
, as shown in the following picture :
The corresponding registration method can refer to the code:file-tree-contribution.ts#L139。
Then define BrowserModule
in the browser/index.ts
file as follows:
import { Provider, Injectable } from '@opensumi/di';
import { BrowserModule } from '@opensumi/ide-core-browser';
import { ExplorerContribution } from './explorer-contribution';
@Injectable()
export class ExplorerModule extends BrowserModule {
providers: Provider[] = [ExplorerContribution];
}
Detailed code reference:explorer/src/browser/index.ts。
In the end, you just need to import this module in the Browser layer and add the registered view ID to the corresponding layout Settings. Take the opensumi/ide-startup project as an example:
Introduce ExplorerModule
in common-modules.ts#L44, Also, go to layout-config.ts#L7 and declare the view rendered under the layout block. As follows:
import { SlotLocation } from '@opensumi/ide-core-browser/lib/react-providers/slot';
import { defaultConfig } from '@opensumi/ide-main-layout/lib/browser/default-config';
export const layoutConfig = {
...defaultConfig,
...{
[SlotLocation.right]: {
modules: ['@opensumi/ide-explorer']
}
}
};
The preceding code declares that the view component registered with ID @OpenSumi/IDE-Explorer
is rendered in the right sidebar area of the IDE.
For more information about view layout, please refer to Custom View document introduction。
Demand Based on Service Capability
View-based requirements generally include service-based demands as well. In general, the OpenSumi framework provides a number of basic capabilities to support various scenarios, such as File Service
, Popup Service
, Storage Service
, etc. Before customizing the related service capabilities, you can see if the desired effect can be achieved through a simple combination of features, if not, then you should consider customizing the service capabilities to meet your needs.
For services such as commands
, menus, keybindings and configurations, we recommend that you use Contribute Points for extensions. The final use is through the following base declaration.
@Injectable()
export class DemoModule extends BrowserModule {
providers: Provider[] = [
...
DemoContribution,
...
];
}
For personalized service capability registration, we recommend you to extend throughDependency Injection, and eventually register by Token + Service
.
@Injectable()
export class DemoModule extends BrowserModule {
providers: Provider[] = [
...
{
token: IDemoService,
useClass: DemoService,
},
...
];
}
Specific practical examples can be found in theclassic case. A basic OpenSumi module generally needs to have the following hierarchical structure:
Hidden Rules About Dependencies
A basic OpenSumi module generally needs to have the following hierarchical structure:
.
└── src
│ ├── browser # optional
│ ├── common
│ └── node # optional
└── webpack.config.js
└── package.json
└── README.md
The following code is our desired dependency structure:
...
"dependencies": {
"@opensumi/ide-core-common": "2.16.10",
"@opensumi/vscode-jsonrpc": "^8.0.0-next.2",
"path-match": "^1.2.4",
"shortid": "^2.2.14",
"ws": "^7.2.0"
},
"devDependencies": {
"@opensumi/ide-components": "2.16.10",
"@opensumi/ide-dev-tool": "^1.3.1",
"mock-socket": "^9.0.2"
}
...
Place the Browser layer and build dependencies in devDependencies
, and Node layer dependencies in dependencies
.
Dependent Structure Diagram
For some of the OpenSumi global dependency structures, the following lists some hidden rules:
-
@opensumi/ide-core-common
is the shared dependency of@opensumi/ide-core-node
,@opensumi/ide-core-browser
and@opensumi/ide-electron
-
Modules do not directly depend on
@opensumi/ide-core-common
, but indirectly depend on@opensumi/ide-core-node
and@opensumi/ide-core-browser
-
Browser resources are usually packaged and built with scripts, while Node resources need to rely directly on
node_modules
. Therefore, we expected the Browser layer dependency of the module to be placed inDevDepedences
in the early design, and Node layer dependency placed onDependences
. -
All modules are built by using
@opensumi/ide-dev-tool
to import dependencies, such astypescript
andwebpack
. -
Based on this directory structure, if there are multiple public dependencies of Browser modules, you can put them in
@opensumi/ide-core-browser
, so as to reduce the version maintenance problems. As a resulit, many public dependencies in the frontend and backend of OpenSumi are declared independently in@opensumi/ide-core-browser
and@opensumi/ide-core-node
respectively. -
In principle, the module
common
can only import content from@opensumi/ide-core-common
, but if this module is a pureBrowser
orNode
module, it can be imported from the corresponding@opensumi/ide-core-browser
and@opensumi/ide-core-node
.
Now that you have an initial understanding of the OpenSumi module, all that is left is to practice and gain more practical experiences. If you have questions about practice, please feel free to submit them to Issue and we will handle your questions promptly.