mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-18 23:07:02 +08:00
docs(editor): add guide for extension usage (#10911)
The guide is baed on #10847 to improve the code quality for migrating extensions using LLM.
This commit is contained in:
@@ -5,6 +5,126 @@ import type { Container } from '@blocksuite/global/di';
|
||||
* Extensions are used to set up the dependency injection container.
|
||||
* In most cases, you won't need to use this class directly.
|
||||
* We provide helper classes like `CommandExtension` and `BlockViewExtension` to make it easier to create extensions.
|
||||
*
|
||||
* # Understanding Extensions
|
||||
*
|
||||
* Extensions provide a way to extend the functionality of a system using dependency injection.
|
||||
* They allow you to register services, implementations, and factories in the DI container,
|
||||
* which can then be retrieved and used by different parts of the application.
|
||||
*
|
||||
* Extensions are particularly useful for:
|
||||
* - Registering different implementations for different types
|
||||
* - Creating pluggable architecture where components can be added or removed
|
||||
* - Managing dependencies between different parts of the application
|
||||
*
|
||||
* # Usage Example: Fruit Processing System
|
||||
*
|
||||
* Let's consider a fruit processing system where different types of fruits need
|
||||
* different processing methods. We'll show how to implement this using extensions.
|
||||
*
|
||||
* ## Step 1: Define the interfaces
|
||||
*
|
||||
* ```ts
|
||||
* interface FruitProcessor {
|
||||
* process(fruit: Fruit): void;
|
||||
* }
|
||||
*
|
||||
* interface Fruit {
|
||||
* type: string;
|
||||
* // other properties
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ## Step 2: Create a service identifier
|
||||
*
|
||||
* ```ts
|
||||
* import { createIdentifier } from '@blocksuite/global/di';
|
||||
*
|
||||
* const FruitProcessorProvider = createIdentifier<FruitProcessor>('fruit-processor-provider');
|
||||
* ```
|
||||
*
|
||||
* ## Step 3: Create implementations
|
||||
*
|
||||
* ```ts
|
||||
* class AppleProcessor implements FruitProcessor {
|
||||
* process(fruit: Fruit): void {
|
||||
* console.log('Slicing apple');
|
||||
* // Apple-specific processing
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* class BananaProcessor implements FruitProcessor {
|
||||
* process(fruit: Fruit): void {
|
||||
* console.log('Peeling banana');
|
||||
* // Banana-specific processing
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ## Step 4: Create an extension factory
|
||||
*
|
||||
* ```ts
|
||||
* const FruitProcessorExtension = (
|
||||
* fruitType: string,
|
||||
* implementation: new () => FruitProcessor
|
||||
* ): ExtensionType => {
|
||||
* return {
|
||||
* setup: di => {
|
||||
* di.addImpl(FruitProcessorProvider(fruitType), implementation);
|
||||
* }
|
||||
* };
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* ## Step 5: Create concrete extensions
|
||||
*
|
||||
* ```ts
|
||||
* export const AppleProcessorExtension = FruitProcessorExtension('apple', AppleProcessor);
|
||||
* export const BananaProcessorExtension = FruitProcessorExtension('banana', BananaProcessor);
|
||||
* ```
|
||||
*
|
||||
* ## Step 6: Use the extensions
|
||||
*
|
||||
* ```ts
|
||||
* import { Container } from '@blocksuite/global/di';
|
||||
*
|
||||
* class FruitProcessingSystem {
|
||||
* provider: ServiceProvider;
|
||||
*
|
||||
* constructor(extensions: ExtensionType[]) {
|
||||
* const container = new Container();
|
||||
*
|
||||
* // Set up all extensions
|
||||
* extensions.forEach(ext => ext.setup(container));
|
||||
*
|
||||
* // Create a provider from the container
|
||||
* this.provider = container.provider();
|
||||
* }
|
||||
*
|
||||
* processFruit(fruit: Fruit) {
|
||||
* // Get the appropriate processor based on fruit type
|
||||
* const processor = this.provider.get(FruitProcessorProvider(fruit.type));
|
||||
*
|
||||
* // Process the fruit
|
||||
* processor.process(fruit);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // Initialize the system with extensions
|
||||
* const system = new FruitProcessingSystem([
|
||||
* AppleProcessorExtension,
|
||||
* BananaProcessorExtension
|
||||
* ]);
|
||||
*
|
||||
* // Use the system
|
||||
* system.processFruit({ type: 'apple' }); // Output: Slicing apple
|
||||
* system.processFruit({ type: 'banana' }); // Output: Peeling banana
|
||||
* ```
|
||||
*
|
||||
* Note: We deliberately used a non-block specific example here. In BlockSuite, the extension
|
||||
* pattern can be applied to any entity that can be configured by third parties, not just blocks.
|
||||
* This includes different tools in the whiteboard, different column types in database blocks,
|
||||
* and many other extensible components. The pattern remains the same regardless of what you're extending.
|
||||
*/
|
||||
export abstract class Extension {
|
||||
static setup(_di: Container): void {
|
||||
|
||||
Reference in New Issue
Block a user