refactor: reorganize package structure and decouple framework packages (#338)

* refactor: reorganize package structure and decouple framework packages

## Package Structure Reorganization
- Reorganized 55 packages into categorized subdirectories:
  - packages/framework/ - Generic framework (Laya/Cocos compatible)
  - packages/engine/ - ESEngine core modules
  - packages/rendering/ - Rendering modules (WASM dependent)
  - packages/physics/ - Physics modules
  - packages/streaming/ - World streaming
  - packages/network-ext/ - Network extensions
  - packages/editor/ - Editor framework and plugins
  - packages/rust/ - Rust WASM engine
  - packages/tools/ - Build tools and SDK

## Framework Package Decoupling
- Decoupled behavior-tree and blueprint packages from ESEngine dependencies
- Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent)
- ESEngine-specific code moved to esengine/ subpath exports
- Framework packages now usable with Cocos/Laya without ESEngine

## CI Configuration
- Updated CI to only type-check and lint framework packages
- Added type-check:framework and lint:framework scripts

## Breaking Changes
- Package import paths changed due to directory reorganization
- ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine')

* fix: update es-engine file path after directory reorganization

* docs: update README to focus on framework over engine

* ci: only build framework packages, remove Rust/WASM dependencies

* fix: remove esengine subpath from behavior-tree and blueprint builds

ESEngine integration code will only be available in full engine builds.
Framework packages are now purely engine-agnostic.

* fix: move network-protocols to framework, build both in CI

* fix: update workflow paths from packages/core to packages/framework/core

* fix: exclude esengine folder from type-check in behavior-tree and blueprint

* fix: update network tsconfig references to new paths

* fix: add test:ci:framework to only test framework packages in CI

* fix: only build core and math npm packages in CI

* fix: exclude test files from CodeQL and fix string escaping security issue
This commit is contained in:
YHH
2025-12-26 14:50:35 +08:00
committed by GitHub
parent a84ff902e4
commit 155411e743
1936 changed files with 4147 additions and 11578 deletions

View File

@@ -0,0 +1,48 @@
{
"name": "@esengine/editor-runtime",
"version": "1.0.0",
"type": "module",
"main": "dist/editor-runtime.js",
"module": "dist/editor-runtime.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/editor-runtime.js"
}
},
"files": [
"dist/**/*"
],
"scripts": {
"build": "tsc && vite build",
"build:watch": "vite build --watch",
"clean": "rimraf dist"
},
"dependencies": {
"@esengine/asset-system": "workspace:*",
"@tauri-apps/api": "^2.2.0",
"@tauri-apps/plugin-dialog": "^2.4.0",
"@tauri-apps/plugin-fs": "^2.4.2",
"tsyringe": "^4.10.0",
"reflect-metadata": "^0.2.2"
},
"devDependencies": {
"@esengine/ecs-framework": "workspace:*",
"@esengine/ecs-framework-math": "workspace:*",
"@esengine/editor-core": "workspace:*",
"@esengine/engine-core": "workspace:*",
"@esengine/fairygui": "workspace:*",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"zustand": "^5.0.8",
"lucide-react": "^0.545.0",
"rimraf": "^5.0.0",
"typescript": "^5.8.3",
"vite": "^6.0.7",
"vite-plugin-dts": "^4.5.0"
},
"private": true
}

View File

@@ -0,0 +1,121 @@
/**
* 文件系统 API
* File System API
*
* 提供统一的文件读写接口,通过后端 Tauri 命令实现。
* 所有插件应使用这些 API 而非直接调用 @tauri-apps/plugin-fs。
*/
import { invoke } from '@tauri-apps/api/core';
/**
* 文件系统操作封装
* 使用后端命令避免前端权限问题
*/
export const FileSystem = {
/**
* 读取文本文件
* @param path 文件路径
* @returns 文件内容
*/
async readTextFile(path: string): Promise<string> {
return await invoke<string>('read_file_content', { path });
},
/**
* 写入文本文件
* @param path 文件路径
* @param content 文件内容
*/
async writeTextFile(path: string, content: string): Promise<void> {
await invoke('write_file_content', { path, content });
},
/**
* 检查路径是否存在
* @param path 文件或目录路径
*/
async exists(path: string): Promise<boolean> {
return await invoke<boolean>('path_exists', { path });
},
/**
* 创建目录
* @param path 目录路径
*/
async createDirectory(path: string): Promise<void> {
await invoke('create_directory', { path });
},
/**
* 创建空文件
* @param path 文件路径
*/
async createFile(path: string): Promise<void> {
await invoke('create_file', { path });
},
/**
* 删除文件
* @param path 文件路径
*/
async deleteFile(path: string): Promise<void> {
await invoke('delete_file', { path });
},
/**
* 删除目录
* @param path 目录路径
*/
async deleteDirectory(path: string): Promise<void> {
await invoke('delete_folder', { path });
},
/**
* 重命名文件或目录
* @param oldPath 原路径
* @param newPath 新路径
*/
async rename(oldPath: string, newPath: string): Promise<void> {
await invoke('rename_file_or_folder', { oldPath, newPath });
},
/**
* 读取目录内容
* @param path 目录路径
* @returns 目录项列表
*/
async readDirectory(path: string): Promise<DirectoryEntry[]> {
return await invoke<DirectoryEntry[]>('read_directory', { path });
},
/**
* 读取文件为 Base64
* @param path 文件路径
* @returns Base64 编码的内容
*/
async readFileAsBase64(path: string): Promise<string> {
return await invoke<string>('read_file_as_base64', { filePath: path });
},
/**
* 写入二进制文件
* @param path 文件路径
* @param data 二进制数据
*/
async writeBinaryFile(path: string, data: Uint8Array): Promise<void> {
await invoke('write_binary_file', { filePath: path, content: Array.from(data) });
}
};
/**
* 目录项
*/
export interface DirectoryEntry {
name: string;
path: string;
isDirectory: boolean;
isFile: boolean;
size?: number;
modified?: number;
}

View File

@@ -0,0 +1,155 @@
/**
* Plugin API - 为插件提供简洁的访问接口
* Plugin API - Provides simple access interface for plugins
*
* 使用方式 | Usage:
* ```typescript
* import { PluginAPI } from '@esengine/editor-runtime';
*
* const scene = PluginAPI.scene;
* const entityStore = PluginAPI.entityStore;
* const messageHub = PluginAPI.messageHub;
*
* // 使用 ServiceToken 获取服务(类型安全)| Get service with ServiceToken (type-safe)
* import { AssetManagerToken } from '@esengine/asset-system';
* const assetManager = PluginAPI.resolve(AssetManagerToken);
* ```
*
* 这个 API 会自动从全局 __ESENGINE_SDK__ 获取正确的实例,
* 避免模块实例不一致的问题。
* This API automatically gets correct instances from global __ESENGINE_SDK__,
* avoiding module instance inconsistency issues.
*/
import type { EntityStoreService, MessageHub } from '@esengine/editor-core';
import type { Scene, ServiceContainer, ServiceToken } from '@esengine/ecs-framework';
/**
* 核心服务接口
* Core service interface
*
* 定义内部 Core 提供的服务访问接口。
* Defines service access interface provided by internal Core.
*/
interface ICoreServices {
services: ServiceContainer;
}
/**
* 内部 API 接口定义
* Internal API interface definition
*
* 定义全局 __ESENGINE_SDK__.api 提供的方法。
* Defines methods provided by global __ESENGINE_SDK__.api.
*/
interface IPluginAPIInternal {
/** 获取当前场景 | Get current scene */
getScene(): Scene | null;
/** 获取实体存储服务 | Get entity store service */
getEntityStore(): EntityStoreService;
/** 获取消息总线 | Get message hub */
getMessageHub(): MessageHub;
/**
* 解析服务(类型安全)
* Resolve service (type-safe)
* @param token 服务令牌 | Service token
*/
resolveService<T>(token: ServiceToken<T>): T;
/** 获取核心实例 | Get core instance */
getCore(): ICoreServices;
}
// 声明全局类型
declare global {
interface Window {
__ESENGINE_SDK__?: {
api?: IPluginAPIInternal;
[key: string]: any;
};
}
}
/**
* 获取内部 API
*/
function getInternalAPI(): IPluginAPIInternal {
const api = window.__ESENGINE_SDK__?.api;
if (!api) {
throw new Error('[PluginAPI] 插件 API 未初始化,请确保编辑器已正确启动');
}
return api;
}
/**
* 插件 API
* 提供简洁的属性访问方式,避免模块实例不一致问题
*/
export const PluginAPI = {
/**
* 获取当前场景
* @throws 如果场景未初始化
*/
get scene(): Scene {
const scene = getInternalAPI().getScene();
if (!scene) {
throw new Error('[PluginAPI] 场景未初始化,请先打开或创建一个场景');
}
return scene;
},
/**
* 获取当前场景(可能为 null
*/
get sceneOrNull(): Scene | null {
return getInternalAPI().getScene();
},
/**
* 获取 EntityStoreService
*/
get entityStore(): EntityStoreService {
return getInternalAPI().getEntityStore();
},
/**
* 获取 MessageHub
*/
get messageHub(): MessageHub {
return getInternalAPI().getMessageHub();
},
/**
* 获取服务容器
*/
get services(): ServiceContainer {
return getInternalAPI().getCore().services;
},
/**
* 解析服务(类型安全)
* Resolve service (type-safe)
*
* 使用 ServiceToken 获取服务实例,提供完整的类型推断。
* Use ServiceToken to get service instance with full type inference.
*
* @param token 服务令牌 | Service token
* @returns 服务实例 | Service instance
*
* @example
* ```typescript
* import { AssetManagerToken } from '@esengine/asset-system';
* const assetManager = PluginAPI.resolve(AssetManagerToken);
* // assetManager 类型自动推断为 IAssetManager
* ```
*/
resolve<T>(token: ServiceToken<T>): T {
return getInternalAPI().resolveService<T>(token);
},
/**
* 检查 API 是否可用
*/
get isAvailable(): boolean {
return !!window.__ESENGINE_SDK__?.api;
},
};

View File

@@ -0,0 +1,238 @@
/**
* Plugin Locale Factory
* 插件国际化工厂
*
* Provides utilities for plugins to create their own locale hooks
* that integrate with the central LocaleService.
*
* 为插件提供创建本地化 hook 的工具函数,
* 这些 hook 会与中央 LocaleService 集成。
*/
import { useState, useEffect, useCallback } from 'react';
import { LocaleService, type Locale, type TranslationParams } from '@esengine/editor-core';
import { Core } from '@esengine/ecs-framework';
/**
* Translation object structure
* 翻译对象结构
*/
export type Translations = {
[key: string]: string | Translations;
};
/**
* Plugin translations bundle
* 插件翻译包
*/
export interface PluginTranslationsBundle<T extends Translations = Translations> {
en: T;
zh: T;
es?: T;
}
/**
* Return type of usePluginLocale hook
* usePluginLocale hook 的返回类型
*/
export interface PluginLocaleHook {
/** Translation function | 翻译函数 */
t: (key: string, params?: TranslationParams) => string;
/** Current locale | 当前语言 */
locale: Locale;
}
/**
* Get nested value from object by dot-separated path
* 通过点分隔路径从对象获取嵌套值
*/
function getNestedValue(obj: Translations, key: string): string | undefined {
const keys = key.split('.');
let current: unknown = obj;
for (const k of keys) {
if (current && typeof current === 'object' && k in current) {
current = (current as Record<string, unknown>)[k];
} else {
return undefined;
}
}
return typeof current === 'string' ? current : undefined;
}
/**
* Interpolate parameters into translation string
* 将参数插入翻译字符串
*/
function interpolate(text: string, params?: TranslationParams): string {
if (!params) return text;
return text.replace(/\{\{(\w+)\}\}/g, (_, key) => {
const value = params[key];
return value !== undefined ? String(value) : `{{${key}}}`;
});
}
/**
* Creates a locale hook for a plugin with its own translations
* 为插件创建一个带有自己翻译的 locale hook
*
* This factory creates a React hook that:
* 1. Syncs with the central LocaleService for locale changes
* 2. Uses plugin-specific translations
* 3. Falls back to English if translation not found
*
* 这个工厂创建一个 React hook它会
* 1. 与中央 LocaleService 同步语言变化
* 2. 使用插件特定的翻译
* 3. 如果找不到翻译则回退到英语
*
* @param translations - Plugin translations bundle | 插件翻译包
* @returns A React hook for accessing translations | 用于访问翻译的 React hook
*
* @example
* ```typescript
* // In your plugin's hooks folder:
* // 在你的插件 hooks 文件夹中:
* import { createPluginLocale } from '@esengine/editor-runtime';
* import { en, zh, es } from '../locales';
*
* export const useTilemapLocale = createPluginLocale({ en, zh, es });
*
* // In your components:
* // 在你的组件中:
* const { t, locale } = useTilemapLocale();
* return <button>{t('toolbar.save')}</button>;
* ```
*/
export function createPluginLocale<T extends Translations>(
translations: PluginTranslationsBundle<T>
): () => PluginLocaleHook {
const allTranslations = {
en: translations.en,
zh: translations.zh,
es: translations.es || translations.en // Fallback to English if no Spanish
};
return function usePluginLocale(): PluginLocaleHook {
const [locale, setLocale] = useState<Locale>('en');
useEffect(() => {
// Try to get LocaleService and sync with it
// 尝试获取 LocaleService 并与之同步
try {
const localeService = Core.services.tryResolve(LocaleService);
if (localeService) {
setLocale(localeService.getCurrentLocale());
// Subscribe to locale changes
// 订阅语言变化
return localeService.onChange((newLocale) => {
setLocale(newLocale);
});
}
} catch {
// LocaleService not available, use default
// LocaleService 不可用,使用默认值
}
}, []);
const t = useCallback((key: string, params?: TranslationParams): string => {
const currentTranslations = allTranslations[locale] || allTranslations.en;
const value = getNestedValue(currentTranslations as Translations, key);
if (value) {
return interpolate(value, params);
}
// Fallback to English if current locale doesn't have the key
// 如果当前语言没有该键,回退到英语
if (locale !== 'en') {
const enValue = getNestedValue(allTranslations.en as Translations, key);
if (enValue) {
return interpolate(enValue, params);
}
}
// Return key itself as last resort
// 最后返回键本身
return key;
}, [locale]);
return { t, locale };
};
}
/**
* Creates a non-React translation function for a plugin
* 为插件创建一个非 React 翻译函数
*
* Use this for translating in non-React contexts (services, utilities, etc.)
* 在非 React 上下文(服务、工具类等)中使用此函数进行翻译
*
* @param translations - Plugin translations bundle | 插件翻译包
* @returns A translation function | 翻译函数
*
* @example
* ```typescript
* // Create translator
* // 创建翻译器
* const translate = createPluginTranslator({ en, zh, es });
*
* // Use in non-React code
* // 在非 React 代码中使用
* const message = translate('errors.notFound', 'en');
* ```
*/
export function createPluginTranslator<T extends Translations>(
translations: PluginTranslationsBundle<T>
): (key: string, locale?: Locale, params?: TranslationParams) => string {
const allTranslations = {
en: translations.en,
zh: translations.zh,
es: translations.es || translations.en
};
return function translate(
key: string,
locale: Locale = 'en',
params?: TranslationParams
): string {
const currentTranslations = allTranslations[locale] || allTranslations.en;
const value = getNestedValue(currentTranslations as Translations, key);
if (value) {
return interpolate(value, params);
}
if (locale !== 'en') {
const enValue = getNestedValue(allTranslations.en as Translations, key);
if (enValue) {
return interpolate(enValue, params);
}
}
return key;
};
}
/**
* Gets the current locale from LocaleService
* 从 LocaleService 获取当前语言
*
* Use this in non-React contexts where you need the current locale
* 在需要当前语言的非 React 上下文中使用
*
* @returns Current locale or 'en' as default | 当前语言或默认 'en'
*/
export function getCurrentLocale(): Locale {
try {
const localeService = Core.services.tryResolve(LocaleService);
if (localeService) {
return localeService.getCurrentLocale();
}
} catch {
// LocaleService not available
}
return 'en';
}

View File

@@ -0,0 +1,19 @@
/**
* Plugin i18n Infrastructure
* 插件国际化基础设施
*
* Exports utilities for plugins to create their own locale systems
* that integrate with the central LocaleService.
*
* 导出供插件创建自己的本地化系统的工具,
* 这些系统会与中央 LocaleService 集成。
*/
export {
createPluginLocale,
createPluginTranslator,
getCurrentLocale,
type Translations,
type PluginTranslationsBundle,
type PluginLocaleHook
} from './createPluginLocale';

View File

@@ -0,0 +1,403 @@
import 'reflect-metadata';
// =============================================================================
// React
// =============================================================================
import * as React from 'react';
import * as ReactDOM from 'react-dom';
export { React, ReactDOM };
export {
useState,
useEffect,
useCallback,
useMemo,
useRef,
useContext,
useReducer,
useLayoutEffect,
useImperativeHandle,
useDebugValue,
useDeferredValue,
useTransition,
useId,
useSyncExternalStore,
useInsertionEffect,
createContext,
forwardRef,
memo,
lazy,
Suspense,
Fragment,
StrictMode,
createElement,
cloneElement,
isValidElement,
Children,
createRef,
Component as ReactComponent,
PureComponent
} from 'react';
export type {
FC,
ReactNode,
ReactElement,
ComponentType,
ComponentProps,
PropsWithChildren,
RefObject,
MutableRefObject,
Dispatch,
SetStateAction,
CSSProperties,
MouseEvent,
KeyboardEvent,
ChangeEvent,
FormEvent,
FocusEvent,
DragEvent
} from 'react';
// =============================================================================
// State Management
// =============================================================================
export { create as createStore } from 'zustand';
export type { StoreApi, UseBoundStore } from 'zustand';
// =============================================================================
// Dependency Injection
// =============================================================================
export { container, injectable, singleton, inject } from 'tsyringe';
export type { DependencyContainer } from 'tsyringe';
// =============================================================================
// ECS Framework Core
// =============================================================================
export * from '@esengine/ecs-framework';
// =============================================================================
// Engine Core (Sorting Layers, Input, etc.)
// =============================================================================
export {
SortingLayers,
SortingLayerManager,
sortingLayerManager,
DEFAULT_SORTING_LAYERS,
Input,
InputManager,
InputSystem,
MouseButton as EngineMouseButton,
} from '@esengine/engine-core';
export type {
ISortable,
SortingLayerConfig,
SortingLayerName,
ISortingLayerManager,
InputSystemConfig,
KeyState,
MouseButtonState,
} from '@esengine/engine-core';
// Re-export vector interfaces from math
export type { IVector2, IVector3 } from '@esengine/ecs-framework-math';
// =============================================================================
// Editor Core
// Rename conflicting exports to avoid collision with ecs-framework
// =============================================================================
export {
EditorComponentRegistry,
} from '@esengine/editor-core';
export type {
IEventBus as IEditorEventBus,
PluginState as EditorPluginState,
PropertyControl as EditorPropertyControl,
PropertyType as EditorPropertyType,
} from '@esengine/editor-core';
// Runtime exports from editor-core
export {
// Enums
PanelPosition,
UIExtensionType,
// Classes
UIRegistry,
MessageHub,
SerializerRegistry,
EntityStoreService,
LocaleService,
ProjectService,
ComponentDiscoveryService,
LogService,
SettingsRegistry,
SceneManagerService,
FileActionRegistry,
EntityCreationRegistry,
CompilerRegistry,
CommandManager,
InspectorRegistry,
PropertyRendererRegistry,
FieldEditorRegistry,
ComponentActionRegistry,
BaseCommand,
PropertyMetadataService,
// Plugin system
PluginManager,
// Gizmo exports
GizmoRegistry,
GizmoColors,
hasGizmoProvider,
hexToGizmoColor,
isGizmoProviderRegistered,
// Symbols (用于跨包插件访问)
IFileSystemService,
IDialogService,
IMessageHub,
ICompilerRegistry,
IInspectorRegistry,
IFileActionRegistry,
} from '@esengine/editor-core';
// Type-only exports from editor-core
export type {
// Plugin types
ISerializer,
FileCreationTemplate,
FileActionHandler,
RegisteredPlugin,
PluginConfig,
LoadingPhase,
PluginState,
// Service interfaces
IFileSystem,
FileEntry,
IDialog,
INotification,
IInspectorProvider,
InspectorContext,
IPropertyRenderer,
IFieldEditor,
FieldEditorContext,
ICompiler,
CompileResult,
CompilerContext,
ICommand,
IEditorDataStore,
// Module interfaces
ICommandRegistry,
IPanelRegistry,
IModuleContext,
IEditorModule,
Unsubscribe,
// Gizmo types
GizmoType,
GizmoColor,
IRectGizmoData,
ICircleGizmoData,
ILineGizmoData,
IGridGizmoData,
IGizmoRenderData,
IGizmoProvider,
GizmoProviderFn,
GizmoProviderRegistration,
// UI types
MenuItem,
ToolbarItem,
PanelDescriptor,
EntityCreationTemplate,
IFileAPI,
PropertyMetadata,
MenuItemDescriptor,
ToolbarItemDescriptor,
ComponentAction,
// Plugin system types
IEditorModuleLoader,
IEditorPlugin,
ModuleManifest,
ModuleCategory,
ModulePlatform,
ModuleExports,
IRuntimePlugin,
IRuntimeModule,
SystemContext,
ComponentInspectorProviderDef,
} from '@esengine/editor-core';
// =============================================================================
// Tauri API (低级 API建议使用 FileSystem 封装)
// =============================================================================
export { invoke, convertFileSrc } from '@tauri-apps/api/core';
export { open, save, message, ask, confirm } from '@tauri-apps/plugin-dialog';
// 注意:以下 API 可能有权限问题,建议使用 FileSystem 替代
export {
readTextFile,
writeTextFile,
readDir,
exists,
mkdir,
remove,
rename,
copyFile
} from '@tauri-apps/plugin-fs';
// =============================================================================
// FileSystem API (推荐使用)
// 通过后端命令实现,避免前端权限问题
// =============================================================================
export { FileSystem, type DirectoryEntry } from './FileSystem';
// =============================================================================
// Icons (Lucide React)
// =============================================================================
import * as Icons from 'lucide-react';
export { Icons };
export type { LucideIcon } from 'lucide-react';
// =============================================================================
// Plugin API
// =============================================================================
export { PluginAPI } from './PluginAPI';
// =============================================================================
// FairyGUI System
// =============================================================================
export {
// ECS Integration
FGUIComponent,
FGUIRenderSystem,
getFGUIRenderSystem,
setFGUIRenderSystem,
FGUIRuntimeModule,
FGUIPlugin,
// Core
GObject,
GComponent,
GRoot,
GGroup,
Controller,
Transition,
Timer,
Stage,
EScaleMode,
EAlignMode,
UIConfig,
getUIConfig,
setUIConfig,
UIObjectFactory,
GObjectPool,
DragDropManager,
// Widgets
GImage,
GTextField,
GGraph,
GButton,
GProgressBar,
GSlider,
GLoader,
GList,
GTextInput,
EKeyboardType,
PopupMenu,
Window,
// Package
UIPackage,
PackageItem,
// Events
EventDispatcher,
FGUIEvents,
// Render
RenderCollector,
RenderBridge,
Canvas2DBackend,
FGUIRenderDataProvider,
createFGUIRenderDataProvider,
// Tween
GTween,
GTweener,
TweenManager,
TweenValue,
evaluateEase,
// Asset
FUIAssetLoader,
fuiAssetLoader,
// Field Types
EButtonMode,
EAutoSizeType,
EAlignType,
EVertAlignType,
ELoaderFillType,
EListLayoutType,
EListSelectionMode,
EOverflowType,
EPackageItemType,
EObjectType,
EProgressTitleType,
EScrollBarDisplayType,
EScrollType,
EFlipType,
EChildrenRenderOrder,
EGroupLayoutType,
EPopupDirection,
ERelationType,
EFillMethod,
EFillOrigin,
EObjectPropID,
EGearType,
EEaseType,
EBlendMode,
ETransitionActionType,
EGraphType,
} from '@esengine/fairygui';
export type {
// FairyGUI types
IFGUIComponentData,
RenderSubmitCallback,
ItemRenderer,
ItemProvider,
IUISource,
TypedEventListener,
EventListener,
FGUIEventType,
IEventContext,
IInputEventData,
IFUIAsset,
IAssetLoader,
IAssetContent,
IAssetParseContext,
IEngineRenderData,
IFGUIRenderDataProvider,
TextureResolverFn,
TweenCallback,
} from '@esengine/fairygui';
// =============================================================================
// Plugin i18n Infrastructure
// =============================================================================
export {
createPluginLocale,
createPluginTranslator,
getCurrentLocale,
type Translations as PluginTranslations,
type PluginTranslationsBundle,
type PluginLocaleHook
} from './i18n';
// =============================================================================
// SDK Metadata
// =============================================================================
export const SDK_VERSION = '1.0.0';
export const SDK_NAME = '@esengine/editor-runtime';

View File

@@ -0,0 +1,37 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ES2020",
"moduleResolution": "bundler",
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"strict": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"composite": true,
"declaration": true,
"declarationMap": true,
"outDir": "dist",
"rootDir": "src",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": [
"src"
],
"references": [
{
"path": "../../framework/core"
},
{
"path": "../../editor/editor-core"
}
]
}

View File

@@ -0,0 +1,63 @@
import { defineConfig } from 'vite';
import { resolve } from 'path';
import dts from 'vite-plugin-dts';
export default defineConfig({
plugins: [
dts({
include: ['src'],
outDir: 'dist',
rollupTypes: false,
tsconfigPath: './tsconfig.json'
})
],
define: {
'process.env.NODE_ENV': JSON.stringify('production')
},
esbuild: {
// 保留类名,用于跨包插件服务匹配
keepNames: true,
},
build: {
lib: {
entry: resolve(__dirname, 'src/index.ts'),
formats: ['es'],
fileName: () => 'editor-runtime.js'
},
rollupOptions: {
// 将 React 和核心 ECS 框架设为外部依赖
// 这确保整个应用使用同一个 ComponentRegistry 实例
external: [
'react',
'react-dom',
'react/jsx-runtime',
'@esengine/ecs-framework',
'@esengine/engine-core', // TransformComponent 等核心组件
'@esengine/ecs-components',
'@esengine/tilemap',
'@esengine/fairygui', // FairyGUI system
'@esengine/behavior-tree',
'@esengine/platform-web',
'@esengine/ecs-engine-bindgen',
'@esengine/asset-system',
'@esengine/sprite', // SpriteComponent
'@esengine/camera', // CameraComponent
'@esengine/material-system',
'@esengine/editor-core', // 编辑器核心(避免 runtime-core → ecs-engine-bindgen 传递依赖)
],
output: {
exports: 'named',
inlineDynamicImports: true,
// 映射外部依赖到全局变量
globals: {
'react': 'React',
'react-dom': 'ReactDOM',
'react/jsx-runtime': 'ReactJSXRuntime'
}
}
},
target: 'es2020',
minify: false,
sourcemap: true
}
});