Files
AFFiNE-Mirror/tsconfig.json
T
Saul-Mirone 70ddae15ef feat(editor): affine extension provider and manager (#11822)
Closes: BS-3186

# @blocksuite/affine-ext-loader

Blocksuite extension loader system for AFFiNE, providing a structured way to manage and load extensions in different contexts.

## Usage

### Basic Extension Provider

```typescript
import { BaseExtensionProvider } from '@blocksuite/affine-ext-loader';
import { z } from 'zod';

// Create a custom provider with options
class MyProvider extends BaseExtensionProvider<'my-scope', { enabled: boolean }> {
  name = 'MyProvider';

  schema = z.object({
    enabled: z.boolean(),
  });

  setup(context: Context<'my-scope'>, options?: { enabled: boolean }) {
    super.setup(context, options);
    // Custom setup logic
  }
}
```

### Store Extensions

```typescript
import { StoreExtensionProvider, StoreExtensionManager } from '@blocksuite/affine-ext-loader';
import { z } from 'zod';

// Create a store provider with custom options
class MyStoreProvider extends StoreExtensionProvider<{ cacheSize: number }> {
  override name = 'MyStoreProvider';

  override schema = z.object({
    cacheSize: z.number().min(0),
  });

  override setup(context: StoreExtensionContext, options?: { cacheSize: number }) {
    super.setup(context, options);
    context.register([Ext1, Ext2, Ext3]);
  }
}

// Create and use the store extension manager
const manager = new StoreExtensionManager([MyStoreProvider]);
manager.configure(MyStoreProvider, { cacheSize: 100 });
const extensions = manager.get('store');
```

### View Extensions

```typescript
import { ViewExtensionProvider, ViewExtensionManager } from '@blocksuite/affine-ext-loader';
import { z } from 'zod';

// Create a view provider with custom options
class MyViewProvider extends ViewExtensionProvider<{ theme: string }> {
  override name = 'MyViewProvider';

  override schema = z.object({
    theme: z.enum(['light', 'dark']),
  });

  override setup(context: ViewExtensionContext, options?: { theme: string }) {
    super.setup(context, options);

    context.register([CommonExt]);
    if (context.scope === 'page') {
      context.register([PageExt]);
    } else if (context.scope === 'edgeless') {
      context.register([EdgelessExt]);
    }
    if (options?.theme === 'dark') {
      context.register([DarkModeExt]);
    }
  }

  // Override effect to run one-time initialization logic
  override effect() {
    // This will only run once per provider class
    console.log('Initializing MyViewProvider');
    // Register lit elements
    this.registerLitElements();
  }
}

// Create and use the view extension manager
const manager = new ViewExtensionManager([MyViewProvider]);
manager.configure(MyViewProvider, { theme: 'dark' });

// Get extensions for different view scopes
const pageExtensions = manager.get('page');
const edgelessExtensions = manager.get('edgeless');
```

### One-time Initialization with Effect

View extensions support one-time initialization through the `effect` method. This method is called automatically during setup, but only once per provider class. It's useful for:

- Initializing global state
- Registering lit elements
- Setting up shared resources

```typescript
class MyViewProvider extends ViewExtensionProvider {
  override effect() {
    // This will only run once, even if multiple instances are created
    initializeGlobalState();
    registerLitElements();
    setupGlobalEventListeners();
  }
}
```

### Available View Scopes

The view extension system supports the following scopes:

- `page` - Standard page view
- `edgeless` - Edgeless (whiteboard) view
- `preview-page` - Page preview view
- `preview-edgeless` - Edgeless preview view
- `mobile-page` - Mobile page view
- `mobile-edgeless` - Mobile edgeless view

### Extension Configuration

Extensions can be configured using the `configure` method:

```typescript
// Set configuration directly
manager.configure(MyProvider, { enabled: true });

// Update configuration using a function
manager.configure(MyProvider, prev => {
  if (!prev) return prev;
  return {
    ...prev,
    enabled: !prev.enabled,
  };
});

// Remove configuration
manager.configure(MyProvider, undefined);
```

### Dependency Injection

Both store and view extension managers support dependency injection:

```typescript
// Access the manager through the di container
const viewManager = std.get(ViewExtensionManagerIdentifier);
const pagePreviewExtension = viewManager.get('preview-page');
```
2025-04-21 04:27:18 +00:00

150 lines
6.2 KiB
JSON

{
"compilerOptions": {
// Strictness
"strict": true,
"verbatimModuleSyntax": true,
"exactOptionalPropertyTypes": false,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noPropertyAccessFromIndexSignature": false,
"noUncheckedIndexedAccess": false,
"skipLibCheck": true,
// Modules
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"typeRoots": ["./tools/@types", "./node_modules/@types"],
// Emit
"lib": ["ES2024"],
"target": "ES2024",
"useDefineForClassFields": false,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"importsNotUsedAsValues": "remove",
// Interop Constraints
"forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true,
"isolatedModules": true,
// Composite
"composite": true,
// NOTE(@forehalo):
// We now "fully" resolve package exports by standard "exports" field in package.json
// but core's exports are lit bit complex, so we left it here and will fix it when repos reorganization is done
"paths": {
"@affine/core/*": ["./packages/frontend/core/src/*"]
}
},
"include": [],
"files": [],
"exclude": ["node_modules", "target", "dist", "lib"],
// NOTE(@forehalo):
// The references are generated by the cli, do not modify it manually
// COMMAND: `yarn affine init`
"references": [
{ "path": "./blocksuite/affine/all" },
{ "path": "./blocksuite/affine/blocks/attachment" },
{ "path": "./blocksuite/affine/blocks/bookmark" },
{ "path": "./blocksuite/affine/blocks/callout" },
{ "path": "./blocksuite/affine/blocks/code" },
{ "path": "./blocksuite/affine/blocks/data-view" },
{ "path": "./blocksuite/affine/blocks/database" },
{ "path": "./blocksuite/affine/blocks/divider" },
{ "path": "./blocksuite/affine/blocks/edgeless-text" },
{ "path": "./blocksuite/affine/blocks/embed" },
{ "path": "./blocksuite/affine/blocks/frame" },
{ "path": "./blocksuite/affine/blocks/image" },
{ "path": "./blocksuite/affine/blocks/latex" },
{ "path": "./blocksuite/affine/blocks/list" },
{ "path": "./blocksuite/affine/blocks/note" },
{ "path": "./blocksuite/affine/blocks/paragraph" },
{ "path": "./blocksuite/affine/blocks/root" },
{ "path": "./blocksuite/affine/blocks/surface" },
{ "path": "./blocksuite/affine/blocks/surface-ref" },
{ "path": "./blocksuite/affine/blocks/table" },
{ "path": "./blocksuite/affine/components" },
{ "path": "./blocksuite/affine/data-view" },
{ "path": "./blocksuite/affine/ext-loader" },
{ "path": "./blocksuite/affine/fragments/doc-title" },
{ "path": "./blocksuite/affine/fragments/frame-panel" },
{ "path": "./blocksuite/affine/fragments/outline" },
{ "path": "./blocksuite/affine/gfx/brush" },
{ "path": "./blocksuite/affine/gfx/connector" },
{ "path": "./blocksuite/affine/gfx/group" },
{ "path": "./blocksuite/affine/gfx/mindmap" },
{ "path": "./blocksuite/affine/gfx/note" },
{ "path": "./blocksuite/affine/gfx/shape" },
{ "path": "./blocksuite/affine/gfx/template" },
{ "path": "./blocksuite/affine/gfx/text" },
{ "path": "./blocksuite/affine/gfx/turbo-renderer" },
{ "path": "./blocksuite/affine/inlines/footnote" },
{ "path": "./blocksuite/affine/inlines/latex" },
{ "path": "./blocksuite/affine/inlines/link" },
{ "path": "./blocksuite/affine/inlines/mention" },
{ "path": "./blocksuite/affine/inlines/preset" },
{ "path": "./blocksuite/affine/inlines/reference" },
{ "path": "./blocksuite/affine/model" },
{ "path": "./blocksuite/affine/rich-text" },
{ "path": "./blocksuite/affine/shared" },
{ "path": "./blocksuite/affine/widgets/drag-handle" },
{ "path": "./blocksuite/affine/widgets/edgeless-auto-connect" },
{ "path": "./blocksuite/affine/widgets/edgeless-toolbar" },
{ "path": "./blocksuite/affine/widgets/frame-title" },
{ "path": "./blocksuite/affine/widgets/keyboard-toolbar" },
{ "path": "./blocksuite/affine/widgets/linked-doc" },
{ "path": "./blocksuite/affine/widgets/remote-selection" },
{ "path": "./blocksuite/affine/widgets/scroll-anchoring" },
{ "path": "./blocksuite/affine/widgets/slash-menu" },
{ "path": "./blocksuite/affine/widgets/toolbar" },
{ "path": "./blocksuite/docs" },
{ "path": "./blocksuite/framework/global" },
{ "path": "./blocksuite/framework/std" },
{ "path": "./blocksuite/framework/store" },
{ "path": "./blocksuite/framework/sync" },
{ "path": "./blocksuite/integration-test" },
{ "path": "./blocksuite/playground" },
{ "path": "./packages/backend/native" },
{ "path": "./packages/backend/server" },
{ "path": "./packages/common/debug" },
{ "path": "./packages/common/env" },
{ "path": "./packages/common/error" },
{ "path": "./packages/common/graphql" },
{ "path": "./packages/common/infra" },
{ "path": "./packages/common/nbstore" },
{ "path": "./packages/common/y-octo/node" },
{ "path": "./packages/frontend/admin" },
{ "path": "./packages/frontend/apps/android" },
{ "path": "./packages/frontend/apps/electron" },
{ "path": "./packages/frontend/apps/electron-renderer" },
{ "path": "./packages/frontend/apps/ios" },
{ "path": "./packages/frontend/apps/mobile" },
{ "path": "./packages/frontend/apps/web" },
{ "path": "./packages/frontend/component" },
{ "path": "./packages/frontend/core" },
{ "path": "./packages/frontend/electron-api" },
{ "path": "./packages/frontend/i18n" },
{ "path": "./packages/frontend/media-capture-playground" },
{ "path": "./packages/frontend/native" },
{ "path": "./packages/frontend/track" },
{ "path": "./tests/affine-cloud" },
{ "path": "./tests/affine-cloud-copilot" },
{ "path": "./tests/affine-desktop" },
{ "path": "./tests/affine-desktop-cloud" },
{ "path": "./tests/affine-local" },
{ "path": "./tests/affine-mobile" },
{ "path": "./tests/blocksuite" },
{ "path": "./tests/kit" },
{ "path": "./tools/cli" },
{ "path": "./tools/playstore-auto-bump" },
{ "path": "./tools/utils" }
]
}