Eulergraph
    Preparing search index...

    Подключение внешних расширений (Engine Extensions)

    Для кого: авторы модулей, расширяющих engine через EngineExtension и ExtensionsService.

    Что получите: lifecycle расширения, свой Stencil, типизированный getModule, сквозной пример.

    Предварительные знания: Быстрый старт, Как создавать библиотеки.

    Внешние расширения — модули, наследующие EngineExtension. Каждое расширение получает собственный экземпляр Stencil и ссылку на eulerEngine. Регистрация — через ExtensionsService и eulerEngine.registerExtensionService().


    import { EngineExtension, ExternalModuleOptions } from "@engine/index"
    import { Library } from "@engine/services/Stencil/libs/Library"
    import { MyShape } from "./shapes/MyShape"

    const myLib = new Library("Моя библиотека", [MyShape])

    export class MyExtension extends EngineExtension {
    constructor(options: ExternalModuleOptions) {
    super(options)
    }

    loadStencil() {
    this.stencil.register(myLib)
    this.stencil.loadLibs()
    }

    doSomething() {
    this.eulerEngine.render()
    }

    dispose(): void {
    super.dispose() // dispose stencil
    }
    }

    EngineExtension создаёт this.stencil = new Stencil(eulerEngine)отдельный от глобального eulerEngine.services.Stencil (если он включён).

    import { MyExtension } from "./MyExtension"

    export const ExtensionClasses = {
    MyExtension,
    } as const

    as const обязателен для точной типизации ключей.

    import * as ExtensionClasses from "./index"

    export const engineExtensionService =
    EulerGraph.registerExternalModules<typeof ExtensionClasses>(ExtensionClasses)
    const myExt = engineExtensionService.getModule("MyExtension")
    // MyExtension | undefined

    myExt?.doSomething()
    • Под капотом все модули регистрируются в рамках одного общего синглтон-сервиса ExtensionsService.
    • Однако на уровне типов TypeScript этот метод возвращает изолированное представление (View), ограниченное исключительно типом T из текущей сессии регистрации.
    • Это позволяет пользователю логически разграничивать контексты:
    // Идеальное автодополнение: mockService «знает» только про моки,
    // а engineExtensionService — только про боевые модули.
    const mockService =
    EulerGraph.registerExternalModules<typeof MockClasses>(MockClasses)
    const engineExtensionService =
    EulerGraph.registerExternalModules<typeof ExtensionClasses>(ExtensionClasses)
    • Если регистрируемый модуль содержит ключи, которые уже были зарегистрированы ранее, будет выброшена ошибка.

    Этап Действие
    Старт host registerExtensionServiceregisterModules
    После Stencil host extension.loadStencil()
    Shutdown engineExtensionService.dispose() — dispose всех модулей
    Модуль super.dispose() — освобождает stencil

    Класс shape должен иметь static readonly typeName и быть в Library, которую регистрирует extension stencil:

    export class GaugeShape extends CellParametric {
    static readonly typeName: string = "GaugeShape"
    // ...
    }

    Library.registereulerGraph.registerElement для каждого shape. См. typeName и сериализация cell-классов.


    • Классы фигур для Stencil должны объявлять static readonly typeName — см. typeName и сериализация cell-классов.

    • Имена модулей в Map совпадают с ключами ExtensionClasses. Свойство name экземпляра перезаписывается при регистрации — не полагайтесь на значение внутри класса.

    • ExtensionsService.dispose() освобождает все зарегистрированные модули.

    • getRegisteredModulesNames() — список ключей для отладки.

    • Extension Stencil и global Stencil — разные экземпляры; библиотеки extension регистрируйте через extension.stencil, не путайте с eulerEngine.services.Stencil.


    • src/renderer/src/external_modules/ — инициализация engineExtensionService
    • src/engine/services/ExtensionsService/EngineExtension.ts